#include "StdAfx.h"
#include "appconfig.h"
#include "md5.h"
#include "Code39.h"
#include "BitmapDC.h"
#include "sha2.h"

// for map drive
#include <winnetwk.h>
#pragma comment(lib, "mpr.lib")

// for AD lookup
#include <activeds.h>
#pragma comment(lib,"Activeds.lib")
#pragma comment(lib,"Adsiid.lib")


CAppConfig::CAppConfig(void)
{
	// init varialbes
	m_nPrintY = 0;
	m_nDaysLeft = 0;
	m_nStaffLevel = 0;
}

CAppConfig::~CAppConfig(void)
{
}

void CAppConfig::ModifyINIString(BOOL bRead, CString Section, CString Item, CString Default, CString* Value)
{
	if( bRead )
	{
		*Value = ReadINIString( Section, Item, Default );
		ReadRegString( Section, Item, Default, Value );
	}
	else
		WriteINIString( Section, Item, *Value );
}

void CAppConfig::ModifyINIInt(BOOL bRead, CString Section, CString Item, INT Default, LPINT Value)
{
	if( bRead )
	{
		*Value = ReadINIInt( Section, Item, Default );
		ReadRegInt( Section, Item, Value );
	}
	else
		WriteINIInt( Section, Item, *Value );

}
void CAppConfig::ModifyINILong(BOOL bRead, CString Section, CString Item, LONG Default, LPLONG Value)
{
	if( bRead )
	{
		*Value = ReadINILong( Section, Item, Default );
		ReadRegLong( Section, Item, Value );
	}
	else
		WriteINILong( Section, Item, *Value );

}
void CAppConfig::ModifyINIBool(BOOL bRead, CString Section, CString Item, BOOL Default, LPBOOL Value)
{
	if( bRead )
	{
		*Value = ReadINIBool( Section, Item, Default );
		ReadRegBool( Section, Item, Value );
	}
	else
		WriteINIBool( Section, Item, *Value );
}

BOOL CAppConfig::WriteINIBool(CString Section, CString Item, BOOL Value)
{
	TCHAR cTemp[256];
	TCHAR cBuffer[8];

	memset(cTemp, NULL, 256);
	memset(cBuffer, NULL, 8);

	if (Value) _tcscpy_s(cBuffer,_T("TRUE"));
	else _tcscpy_s(cBuffer,_T("FALSE"));

	return WritePrivateProfileString(Section, Item, cBuffer, m_INIPath);
}

BOOL CAppConfig::WriteINIString(CString Section, CString Item, CString Value)
{
	return WritePrivateProfileString(Section, Item, Value, m_INIPath);
}

BOOL CAppConfig::WriteINIInt(CString Section, CString Item, INT Value)
{
	CString csValue;
	
	csValue.Format(_T("%d"), Value );
	return WritePrivateProfileString(Section, Item, csValue, m_INIPath);
}

BOOL CAppConfig::WriteINILong(CString Section, CString Item, LONG Value)
{
	CString csValue;

	csValue.Format(_T("%ld"), Value );
	return WritePrivateProfileString(Section, Item, csValue, m_INIPath);
}

CString CAppConfig::ReadINIString(CString Section, CString Item, CString Default)
{
	TCHAR cTemp[2048];

	memset(cTemp, NULL, 2048);

	GetPrivateProfileString(Section, Item,_T("TRUE"), cTemp, sizeof(cTemp), m_INIPath);
	if (!_tcsicmp(cTemp,_T("TRUE"))) return Default;
	else if (!_tcsicmp(cTemp,_T("FALSE"))) return _T("");
	else return cTemp;
}

INT CAppConfig::ReadINIInt(CString Section, CString Item, INT Default)
{
	TCHAR cTemp[256];
	TCHAR cBuffer[32];

	memset(cTemp, NULL, 256);
	memset(cBuffer, NULL, 32);

	_itot_s(Default, cBuffer, 10);

	GetPrivateProfileString(Section, Item, cBuffer, cTemp, sizeof(cTemp), m_INIPath);
	if (!_tcsicmp(cTemp,_T("TRUE"))) return Default;
	else if (!_tcsicmp(cTemp,_T("FALSE"))) return NULL;
	else return _tstoi(cTemp);
}

LONG CAppConfig::ReadINILong(CString Section, CString Item, LONG Default)
{
	TCHAR cTemp[256];
	TCHAR cBuffer[64];

	memset(cTemp, NULL, 256);
	memset(cBuffer, NULL, 64);

	_ltot_s(Default, cBuffer, 10);

	GetPrivateProfileString(Section, Item, cBuffer, cTemp, sizeof(cTemp), m_INIPath);
	if (!_tcsicmp(cTemp,_T(""))) return Default;
	else if (!_tcsicmp(cTemp,_T("TRUE"))) return Default;
	else if (!_tcsicmp(cTemp,_T("FALSE"))) return NULL;
	else return _tstol(cTemp);
}

BOOL CAppConfig::ReadINIBool(CString Section, CString Item)
{
	TCHAR cTemp[256];

	memset(cTemp, NULL, 256);

	GetPrivateProfileString(Section, Item,_T("TRUE"), cTemp, sizeof(cTemp), m_INIPath);
	if (!_tcsicmp(cTemp,_T("TRUE"))) return TRUE;
	else return FALSE;	
}

BOOL CAppConfig::ReadINIBool(CString Section, CString Item, BOOL Default)
{
	TCHAR cTemp[256];
	TCHAR cBuffer[8];

	memset(cTemp, NULL, 8);
	memset(cBuffer, NULL, 8);

	if (Default) _tcscpy_s(cBuffer,_T("TRUE"));
	else _tcscpy_s(cBuffer,_T("FALSE"));

	GetPrivateProfileString(Section, Item, cBuffer, cTemp, sizeof(cTemp), m_INIPath);
	if (!_tcsicmp(cTemp,_T("TRUE"))) return TRUE;
	else return FALSE;	
}

void CAppConfig::ReadRegString(CString Section, CString Item, CString Default, CString* Value)
{
	CRegKey key;
	TCHAR sTemp[1024];
	ULONG uTempLen = 1024;

	memset( sTemp, NULL, uTempLen );
	// open the registry location
	if( key.Open( HKEY_LOCAL_MACHINE, _T("Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
		{
			if (!_tcsicmp(sTemp,_T("TRUE"))) *Value = Default;
			else if (!_tcsicmp(sTemp,_T("FALSE"))) *Value = _T("");
			else *Value = sTemp;
		}
	key.Close();

	uTempLen = 1024;
	// open the registry location
	if ( key.Open( HKEY_CURRENT_USER, _T("Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
		{
			if (!_tcsicmp(sTemp,_T("TRUE"))) *Value = Default;
			else if (!_tcsicmp(sTemp,_T("FALSE"))) *Value = _T("");
			else *Value = sTemp;
		}
	key.Close();
}

void CAppConfig::ReadRegInt(CString Section, CString Item, LPINT Value)
{
	CRegKey key;
	TCHAR sTemp[1024];
	ULONG uTempLen = 1024;

	memset( sTemp, NULL, uTempLen );
	// open the registry location
	if ( key.Open( HKEY_LOCAL_MACHINE, _T( "Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
			*Value = _tstoi( sTemp );		
	key.Close();

	uTempLen = 1024;
	// open the registry location
	if ( key.Open( HKEY_CURRENT_USER, _T( "Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
			*Value = _tstoi( sTemp );
	key.Close();
}

void CAppConfig::ReadRegLong(CString Section, CString Item, LPLONG Value)
{
	CRegKey key;
	TCHAR sTemp[1024];
	ULONG uTempLen = 1024;

	memset( sTemp, NULL, uTempLen );
	// open the registry location
	if ( key.Open( HKEY_LOCAL_MACHINE, _T( "Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
			*Value = _tstol( sTemp );		
	key.Close();

	uTempLen = 1024;
	// open the registry location
	if ( key.Open( HKEY_CURRENT_USER, _T( "Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
			*Value = _tstol( sTemp );
	key.Close();
}

void CAppConfig::ReadRegBool(CString Section, CString Item, LPBOOL Value)
{
	CRegKey key;
	TCHAR sTemp[1024];
	ULONG uTempLen = 1024;

	memset( sTemp, NULL, uTempLen );
	// open the registry location
	if ( key.Open( HKEY_LOCAL_MACHINE, _T( "Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
		{
			if (!_tcsicmp(sTemp,_T("TRUE"))) *Value = TRUE;
			else *Value = FALSE;
		}			
	key.Close();

	uTempLen = 1024;
	// open the registry location
	if ( key.Open( HKEY_CURRENT_USER, _T( "Software\\TeamSoftware Solutions\\Celsus\\" + Section )) == ERROR_SUCCESS )
		if( key.QueryStringValue( Item, sTemp, &uTempLen ) == ERROR_SUCCESS )
		{
			if (!_tcsicmp(sTemp,_T("TRUE"))) *Value = TRUE;
			else *Value = FALSE;
		}			
	key.Close();
}

INT_PTR CAppConfig::ReadINISectionKeys(CStringArray &csaKeys, CString Section)
{
	TCHAR tczTemp[2048];
	CString csTemp;
	INT ix = 0;
	INT_PTR ic = 0;
  
	// Get the keys from section
	GetPrivateProfileSection( Section, tczTemp, sizeof( tczTemp ), m_INIPath );

	// The section's key=value lines are separated by nulls and stored in tczTemp.
	// Iterate through those keys. The section is terminated by 2 NULLs.
	while(tczTemp[ ix ])
	{
		// Copy the current line to csTemp.
		csTemp = &tczTemp[ix ];

		// Add to string array
		ic = csaKeys.Add(csTemp);

		// adjust the current index.  Adding 1 gives you the index to the first
		// character beyond the NULL separator
		ix += csTemp.GetLength() + 1;

		// If there's a NULL at _tczTemp[ ix ], we're done iterating through
		// the section.
	}

	return ic;
}

INT CAppConfig::ReadConfig(void)
{
	// reading
	Config( TRUE );
	// for future use
	return 0;
}

INT CAppConfig::WriteConfig(void)
{
	// writing
	Config( FALSE );
	// for future use
	return 0;
}

void CAppConfig::Config(BOOL bRead)
{
	// [General]
	ModifyINIString( bRead,_T("General"),_T("Location"),_T(""), &m_General.szLocation );
	ModifyINIString( bRead,_T("General"),_T("RootFolder"),_T(""), &m_General.szRootFolder );
	ModifyINIString( bRead,_T("General"),_T("DirectURL"),_T(""), &m_General.szDirectURL );	
	ModifyINIString( bRead,_T("General"),_T("RootWebPHP"),_T(""), &m_General.szRootWebPHP );
	ModifyINIString( bRead,_T("General"),_T("RootWebAdmin"),_T(""), &m_General.szRootWebAdmin );
	ModifyINIString( bRead,_T("General"),_T("UserSite"),_T(""), &m_General.szUserSite );
	ModifyINIString( bRead,_T("General"),_T("WEBLink"),_T(""), &m_General.szWEBLink );
	ModifyINIString( bRead,_T("General"),_T("WEBNALink"),_T(""), &m_General.szWEBNALink );
	ModifyINIString( bRead,_T("General"),_T("FTPLink"),_T(""), &m_General.szFTPLink );
	ModifyINIString( bRead,_T("General"),_T("LookupLink"),_T(""), &m_General.szLookupLink );
	ModifyINIString( bRead,_T("General"),_T("SourceLink"),_T(""), &m_General.szSourceLink );
	ModifyINIString( bRead,_T("General"),_T("SendHtmlFileLink"),_T(""), &m_General.szSendHtmlFileLink );
	ModifyINIString( bRead,_T("General"),_T("WorkerLink"),_T(""), &m_General.szWorkerLink );
	ModifyINIString( bRead,_T("General"),_T("PUBMEDLink"),_T(""), &m_General.szPUBMEDLink );
	ModifyINIString( bRead,_T("General"),_T("StatusIconPath"),_T(""), &m_General.szStatusIconPath );	
	ModifyINIBool( bRead,_T("General"),_T("RemoveFileAfterSend"), FALSE, &m_General.bRemoveFileAfterSend );
	ModifyINIString( bRead,_T("General"),_T("WebAttachFolder"),_T(""), &m_General.szWebAttachFolder );
	ModifyINIBool( bRead,_T("General"),_T("WriteHistory"), FALSE, &m_General.bWriteHistory );
	ModifyINIString( bRead,_T("General"),_T("HistoryFile"),_T(""), &m_General.szHistoryFile );
	ModifyINIString( bRead,_T("General"),_T("InformationFile"),_T(""), &m_General.szInformationFile );
	ModifyINIString( bRead,_T("General"),_T("HoldDateFile"),_T(""), &m_General.szHoldDateFile );	
	ModifyINIString( bRead,_T("General"),_T("InformationFileSQL"),_T(""), &m_General.szInformationFileSQL );	
	ModifyINIString( bRead,_T("General"),_T("EnvironmentVariable"),_T(""), &m_General.szEnvironmentVariable );
	ModifyINIBool( bRead,_T("General"),_T("UseUniqueHistoryFiles"), FALSE, &m_General.bUseUniqueHistoryFiles );
	ModifyINIInt( bRead,_T("General"),_T("DeleteFilesOverDays"), 14, &m_General.nDeleteFilesOverDays );
	ModifyINIInt( bRead,_T("General"),_T("AdvertiseDaysOld"), 10, &m_General.nAdvertiseDaysOld );
	ModifyINIString( bRead,_T("General"),_T("AdvertiseDateFormat"),_T(""), &m_General.szAdvertiseDateFormat );
	ModifyINIBool( bRead,_T("General"),_T("RemoveEmptyFolders"), TRUE, &m_General.bRemoveEmptyFolders );
	ModifyINIBool( bRead,_T("General"),_T("EnableDeleteHistory"), FALSE, &m_General.bEnableDeleteHistory );
	ModifyINIBool( bRead,_T("General"),_T("EnableOpenHistory"), FALSE, &m_General.bEnableOpenHistory );
	ModifyINIString( bRead,_T("General"),_T("FileDescriptionPrefix"),_T(""), &m_General.szFileDescriptionPrefix );
	ModifyINIString( bRead,_T("General"),_T("DefaultDescription"),_T(""), &m_General.szDefaultDescription );
	ModifyINIString( bRead,_T("General"),_T("RequesterInfoFile"),_T(""), &m_General.szRequesterInfoFile );
	ModifyINIString( bRead,_T("General"),_T("RequestNoteFile"),_T(""), &m_General.szRequestNoteFile );
	ModifyINIString( bRead,_T("General"),_T("RequestFile"),_T(""), &m_General.szRequestFile );
	ModifyINIString( bRead,_T("General"),_T("SendInfoFile"),_T(""), &m_General.szSendInfoFile );
	ModifyINIInt( bRead,_T("General"),_T("StartHeight"), 0, &m_General.nStartHeight );
	ModifyINIInt( bRead,_T("General"),_T("StartWidth"), 0, &m_General.nStartWidth );
	ModifyINIBool( bRead,_T("General"),_T("StartWebView"), FALSE, &m_General.bStartWebView );
	ModifyINIString( bRead,_T("General"),_T("DateFormat"),_T("%d-%b-%Y   %I:%M %p"), &m_General.szDateFormat );
	ModifyINIString( bRead,_T("General"),_T("ShortDateFormat"),_T("%A, %B %d, %Y"), &m_General.szShortDateFormat );
	ModifyINIBool( bRead,_T("General"),_T("StoreFilesInDataBase"), FALSE, &m_General.bStoreFilesInDataBase );
	ModifyINIBool( bRead,_T("General"),_T("SendFilesViaHTTP"), FALSE, &m_General.bSendFilesViaHTTP );
	ModifyINIBool( bRead,_T("General"),_T("SendFilesViaFTP"), FALSE, &m_General.bSendFilesViaFTP );
	ModifyINIString( bRead,_T("General"),_T("FileTypesFormat"),_T(""), &m_General.szFileTypesFormat );
	ModifyINIBool( bRead,_T("General"),_T("FileCopyNoUI"), FALSE, &m_General.bFileCopyNoUI );
	ModifyINIInt( bRead,_T("General"),_T("DefaultDaysSearch"), 0, &m_General.nDefaultDaysSearch );
	ModifyINIInt( bRead,_T("General"),_T("DaysShownComplete"), 0, &m_General.nDaysShownComplete );	
	ModifyINIInt( bRead,_T("General"),_T("HoursClaimLimit"), 0, &m_General.nHoursClaimLimit );	
	ModifyINIString( bRead,_T("General"),_T("PasswordHash"),_T("md5"), &m_General.szPasswordHash );
	ModifyINIBool( bRead,_T("General"),_T("TestSystem"), FALSE, &m_General.bTestSystem );
	ModifyINIBool( bRead,_T("General"),_T("UseSitckyCollection"), FALSE, &m_General.bUseSitckyCollection );
	ModifyINIBool( bRead,_T("General"),_T("UseAutoLinkSourcing"), FALSE, &m_General.bUseAutoLinkSourcing );
	ModifyINIBool( bRead,_T("General"),_T("UseStickyNAReason"), FALSE, &m_General.bUseStickyNAReason );
	ModifyINIString( bRead,_T("General"),_T("RequestAddFile"),_T(""), &m_General.szRequestAddFile );
		
	// [LDAP]
	ModifyINIBool( bRead,_T("LDAP"),_T("UseLDAPLogons"), FALSE, &m_LDAP.bUseLDAPLogons );
	ModifyINIBool( bRead,_T("LDAP"),_T("AutoAddStaff"), FALSE, &m_LDAP.bAutoAddStaff );
	ModifyINIString( bRead,_T("LDAP"),_T("Server"),_T(""), &m_LDAP.szServer );
	ModifyINIString( bRead,_T("LDAP"),_T("UserName"),_T(""), &m_LDAP.szUserName );
	ModifyINIString( bRead,_T("LDAP"),_T("Password"),_T(""), &m_LDAP.szPassword );

	// [Printing]
	ModifyINIInt( bRead,_T("Printing"),_T("RequestsPerPage"), 0, &m_Printing.nRequestsPerPage );
	ModifyINIString( bRead,_T("Printing"),_T("RequestTemplate"),_T(""), &m_Printing.szRequestTemplate );
	ModifyINIString( bRead,_T("Printing"),_T("TemplatePageBreak"),_T(""), &m_Printing.szTemplatePageBreak );
	ModifyINIBool( bRead,_T("Printing"),_T("PrintPreview"), FALSE, &m_Printing.bPrintPreview );	
	ModifyINIString( bRead, _T( "Printing" ), _T( "Footer" ), _T( "&u&b&d" ), &m_Printing.szFooter);
	ModifyINIString( bRead, _T( "Printing" ), _T( "Header" ), _T( "&w&bPage &p of &P" ), &m_Printing.szHeader);
	ModifyINIString( bRead, _T( "Printing" ), _T( "MarginBottom" ), _T( "0.75000" ), &m_Printing.szMarginBottom);
	ModifyINIString( bRead, _T( "Printing" ), _T( "MarginLeft" ), _T( "0.75000" ), &m_Printing.szMarginLeft);
	ModifyINIString( bRead, _T( "Printing" ), _T( "MarginRight" ), _T( "0.75000" ), &m_Printing.szMarginRight);
	ModifyINIString( bRead, _T( "Printing" ), _T( "MarginTop" ), _T( "0.75000" ), &m_Printing.szMarginTop);

	// [Options]
	ModifyINIBool( bRead,_T("Options"),_T("DisableMyView"), FALSE, &m_Options.bDisableMyView );
	ModifyINIBool( bRead,_T("Options"),_T("DisableILL"), FALSE, &m_Options.bDisableILL );
	ModifyINIBool( bRead,_T("Options"),_T("DisableAtLibrary"), FALSE, &m_Options.bDisableAtLibrary );
	ModifyINIBool( bRead,_T("Options"),_T("UseComapnyForLocation"), FALSE, &m_Options.bUseComapnyForLocation );
	ModifyINIBool( bRead,_T("Options"),_T("DisableFinacial"), FALSE, &m_Options.bDisableFinacial );
	ModifyINIBool( bRead,_T("Options"),_T("DisableReferrals"), FALSE, &m_Options.bDisableReferrals );
	ModifyINIBool( bRead,_T("Options"),_T("DisableCopyright"), FALSE, &m_Options.bDisableCopyright );
	ModifyINIBool( bRead,_T("Options"),_T("DisableProject"), FALSE, &m_Options.bDisableProject );
	ModifyINIBool( bRead,_T("Options"),_T("DisableFileSendManual"), FALSE, &m_Options.bDisableFileSendExternally );
	ModifyINIBool( bRead,_T("Options"),_T("DisableTriageProcess"), FALSE, &m_Options.bDisableTriageProcess );
	ModifyINIBool( bRead,_T("Options"),_T("DisableSourceLinkCheck"), FALSE, &m_Options.bDisableSourceLinkCheck );
	ModifyINIBool( bRead,_T("Options"),_T("DisableCombineNASendReturn"), FALSE, &m_Options.bDisableCombineNASendReturn );
			
	// [SFX]
	ModifyINIString( bRead,_T("SFX"),_T("TitleStartSearch"),_T(""), &m_SFX.szTitleStartSearch );	
	ModifyINIString( bRead,_T("SFX"),_T("TitleMidSearch"),_T(""), &m_SFX.szTitleMidSearch );	
	ModifyINIString( bRead,_T("SFX"),_T("TitleEndSearch"),_T(""), &m_SFX.szTitleEndSearch );	
	ModifyINIString( bRead,_T("SFX"),_T("SourceStartSearch"),_T(""), &m_SFX.szSourceStartSearch );	
	ModifyINIString( bRead,_T("SFX"),_T("SourceMidSearch"),_T(""), &m_SFX.szSourceMidSearch );	
	ModifyINIString( bRead,_T("SFX"),_T("SourceEndSearch"),_T(""), &m_SFX.szSourceEndSearch );
	ModifyINIString( bRead,_T("SFX"),_T("SourceEndString"),_T(" ["), &m_SFX.szSourceEndString );	

	// [Colors]
	CString szTemp;
	ModifyINIString( bRead,_T("Colors"),_T("TriageHighlight"),_T("#FFC0FF"), &szTemp );
	m_Colors.dwTriageHighlight = GetColorRef( szTemp );
	ModifyINIString( bRead,_T("Colors"),_T("SortHighlight"),_T("#E0E0E0"), &szTemp );
	m_Colors.dwSortHighlight = GetColorRef( szTemp );
	ModifyINIString( bRead,_T("Colors"),_T("DuplicateHighlight"),_T("#C0FFC0"), &szTemp );
	m_Colors.dwDuplicateHighlight = GetColorRef( szTemp );
	ModifyINIString( bRead,_T("Colors"),_T("MaterialLicensed"),_T("#C0FFFF"), &szTemp );
	m_Colors.dwMaterialLicensed = GetColorRef( szTemp );
	ModifyINIString( bRead,_T("Colors"),_T("MaterialFree"),_T("#FFFFC0"), &szTemp );
	m_Colors.dwMaterialFree = GetColorRef( szTemp );
	ModifyINIString( bRead,_T("Colors"),_T("MaterialNotFree"),_T("#E62E00"), &szTemp );
	m_Colors.dwMaterialNotFree = GetColorRef( szTemp );
	ModifyINIString( bRead,_T("Colors"),_T("LibraryListed"),_T("#E1FFFF"), &szTemp );
	m_Colors.dwLibraryListed = GetColorRef( szTemp );

	// [Database]
	ModifyINIString( bRead,_T("Database"),_T("SourceName"),_T(""), &m_Database.szSourceName );
	ModifyINIString( bRead,_T("Database"),_T("Driver"),_T(""), &m_Database.szDriver );
	ModifyINIString( bRead,_T("Database"),_T("Database"),_T(""), &m_Database.szDatabase );
	ModifyINIString( bRead,_T("Database"),_T("Server"),_T(""), &m_Database.szServer );
	ModifyINIString( bRead,_T("Database"),_T("Port"),_T(""), &m_Database.szPort );
	ModifyINIString( bRead,_T("Database"),_T("UID"),_T(""), &m_Database.szUID );
	ModifyINIString( bRead,_T("Database"),_T("PWD"),_T(""), &m_Database.szPWD );
	ModifyINIInt( bRead,_T("Database"),_T("Limit"), 500, &m_Database.nLimit );
	ModifyINIInt( bRead,_T("Database"),_T("Range"), 100, &m_Database.nRange );
	ModifyINIInt( bRead,_T("Database"),_T("RefreshTime"), 30, &m_Database.nRefreshTime );
	ModifyINIBool( bRead,_T("Database"),_T("UpdateOnLogin"), FALSE, &m_Database.bUpdateOnLogin );

	// [ExternalDatabase]
	ModifyINIString( bRead,_T("External"),_T("Database"),_T(""), &m_External.szDatabase );
	ModifyINIString( bRead,_T("External"),_T("Server"),_T(""), &m_External.szServer );
	ModifyINIString( bRead,_T("External"),_T("Port"),_T(""), &m_External.szPort );
	ModifyINIString( bRead,_T("External"),_T("UID"),_T(""), &m_External.szUID );
	ModifyINIString( bRead,_T("External"),_T("PWD"),_T(""), &m_External.szPWD );
	ModifyINIString( bRead,_T("External"),_T("FTPServer"),_T(""), &m_External.szFTPServer );
	ModifyINIString( bRead,_T("External"),_T("FTPFolder"),_T(""), &m_External.szFTPFolder );
	ModifyINIString( bRead,_T("External"),_T("FTPUID"),_T(""), &m_External.szFTPUID );
	ModifyINIString( bRead,_T("External"),_T("FTPPWD"),_T(""), &m_External.szFTPPWD );
	ModifyINIString( bRead,_T("External"),_T("WEBLink"),_T(""), &m_External.szWEBLink );
	ModifyINIString( bRead,_T("External"),_T("WEBNALink"),_T(""), &m_External.szWEBNALink );
	ModifyINIString( bRead,_T("External"),_T("UserSite"),_T(""), &m_External.szUserSite );	

	// [Queue]
	ModifyINIBool( bRead,_T("Queue"),_T("EnableQueue"), FALSE, &m_Queue.bEnableQueue );
	ModifyINIString( bRead,_T("Queue"),_T("QueueFolder"),_T(""), &m_Queue.szQueueFolder );
	ModifyINIBool( bRead,_T("Queue"),_T("ShowQueueErrors"), FALSE, &m_Queue.bShowQueueErrors );
	ModifyINIString( bRead,_T("Queue"),_T("QueueErrorEmail"),_T(""), &m_Queue.szQueueErrorEmail );
	ModifyINIBool( bRead,_T("Queue"),_T("ShowCopyDialog"), FALSE, &m_Queue.bShowCopyDialog );
	
	// [Email]
	ModifyINIString( bRead,_T("Email"),_T("ContactInfo"),_T(""), &m_Email.szContactInfo );
	ModifyINIString( bRead,_T("Email"),_T("RequesterDomain"),_T(""), &m_Email.szRequesterDomain );
	ModifyINIString( bRead,_T("Email"),_T("DefaultRequesterName"),_T("Celsus User"), &m_Email.szDefaultRequesterName );	
	ModifyINIString( bRead,_T("Email"),_T("ReturnEmailName"),_T(""), &m_Email.szReturnEmailName );
    ModifyINIString( bRead,_T("Email"),_T("ReturnEmailAddress"),_T(""), &m_Email.szReturnEmailAddress );
	ModifyINIString( bRead,_T("Email"),_T("HoldNoticeEmailAddress"),_T(""), &m_Email.szHoldNoticeEmailAddress );
	ModifyINIString( bRead,_T("Email"),_T("EmailSubject"),_T(""), &m_Email.szEmailSubject );
	ModifyINIString( bRead,_T("Email"),_T("EmailSubjectNotAvailable"),_T(""), &m_Email.szEmailSubjectNotAvailable );
	ModifyINIString( bRead,_T("Email"),_T("EmailSubjectPickupNotice"),_T(""), &m_Email.szEmailSubjectPickupNotice );
	ModifyINIString( bRead,_T("Email"),_T("EmailSubjectInfoLink"),_T(""), &m_Email.szEmailSubjectInfoLink );
	ModifyINIString( bRead,_T("Email"),_T("EmailSubjectMoveILL"),_T(""), &m_Email.szEmailSubjectMoveILL );	
	ModifyINIString( bRead,_T("Email"),_T("EmailSubjectHold"),_T(""), &m_Email.szEmailSubjectHold );		
	ModifyINIString( bRead,_T("Email"),_T("ArticleAvailable"),_T(""), &m_Email.szArticleAvailable );
	ModifyINIString( bRead,_T("Email"),_T("ArticleSomeAvailable"),_T(""), &m_Email.szArticleSomeAvailable );
	ModifyINIString( bRead,_T("Email"),_T("ArticleNoneAvailable"),_T(""), &m_Email.szArticleNoneAvailable );
    ModifyINIString( bRead,_T("Email"),_T("NotAvailableMessage"),_T(""), &m_Email.szNotAvailableMessage );
	ModifyINIString( bRead,_T("Email"),_T("SendMessageFile"),_T(""), &m_Email.szSendMessageFile );
	ModifyINIString( bRead,_T("Email"),_T("SendMessageFileNA"),_T(""), &m_Email.szSendMessageFileNA );
	ModifyINIString( bRead,_T("Email"),_T("SendMessageFileNA1"),_T(""), &m_Email.szSendMessageFileNA1 );	
	ModifyINIString( bRead,_T("Email"),_T("SendMessageFileNA2"),_T(""), &m_Email.szSendMessageFileNA2 );
	ModifyINIString( bRead,_T("Email"),_T("LinkMessageFile"),_T(""), &m_Email.szLinkMessageFile );	
	ModifyINIString( bRead,_T("Email"),_T("NoteMessageFile"),_T(""), &m_Email.szNoteMessageFile );		
	ModifyINIString( bRead,_T("Email"),_T("AttachMessageFile"),_T(""), &m_Email.szAttachMessageFile );
	ModifyINIString( bRead,_T("Email"),_T("RemindMessageFile"),_T(""), &m_Email.szRemindMessageFile );	
	ModifyINIString( bRead,_T("Email"),_T("FTPMessageFile"),_T(""), &m_Email.szFTPMessageFile );
	ModifyINIString( bRead,_T("Email"),_T("FTPManualFile"),_T(""), &m_Email.szFTPManualFile );
	ModifyINIString( bRead,_T("Email"),_T("AttachManualFile"),_T(""), &m_Email.szAttachManualFile );
	
	ModifyINIString( bRead,_T("Email"),_T("NonEDDMessageFile"),_T(""), &m_Email.szNonEDDMessageFile );
	ModifyINIString( bRead,_T("Email"),_T("MoveILLMessageFile"),_T(""), &m_Email.szMoveILLMessageFile );
	ModifyINIString( bRead,_T("Email"),_T("HoldMessageFile"),_T(""), &m_Email.szHoldMessageFile );
	ModifyINIString( bRead,_T("Email"),_T("HoldNoticeFile"),_T(""), &m_Email.szHoldNoticeFile );	
	ModifyINIString( bRead,_T("Email"),_T("AddMessageFile"),_T(""), &m_Email.szAddMessageFile );

	ModifyINIString( bRead,_T("Email"),_T("SMTPServer"),_T(""), &m_Email.szSMTPServer );
	ModifyINIInt( bRead,_T("Email"),_T("SMTPPort"), 25, &m_Email.nSMTPPort );
	ModifyINIString( bRead,_T("Email"),_T("POPServer"),_T(""), &m_Email.szPOPServer );
	ModifyINIInt( bRead,_T("Email"),_T("POPPort"), 110, &m_Email.nPOPPort );
	ModifyINIString( bRead,_T("Email"),_T("EmailUser"),_T(""), &m_Email.szEmailUser );
	ModifyINIString( bRead,_T("Email"),_T("EmailPassword"),_T(""), &m_Email.szEmailPassword );
	ModifyINIString( bRead,_T("Email"),_T("FailureSubject"),_T(""), &m_Email.szFailureSubject );
	ModifyINIInt( bRead,_T("Email"),_T("MaxFailureCheck"), 100, &m_Email.nMaxFailureCheck );
	ModifyINIString( bRead,_T("Email"),_T("FailureEmailHead"),_T(""), &m_Email.szFailureEmailHead );
	ModifyINIString( bRead,_T("Email"),_T("FailureEmailFoot"),_T(""), &m_Email.szFailureEmailFoot );
	ModifyINIInt( bRead,_T("Email"),_T("AttachmentmaxBytes"), 5000, &m_Email.nAttachmentmaxBytes );
	ModifyINIInt( bRead,_T("Email"),_T("FileSizeWarningBytes"), 10000, &m_Email.nFileSizeWarningBytes );
	ModifyINIString( bRead,_T("Email"),_T("WebLinkAvailableText"),_T(""), &m_Email.szWebLinkAvailableText );
	ModifyINIString( bRead,_T("Email"),_T("WebLinkNoneAvailableText"),_T(""), &m_Email.szWebLinkNoneAvailableText );
	ModifyINIBool( bRead,_T("Email"),_T("SendToAlternateEmail"), FALSE, &m_Email.bSendToAlternateEmail );
	ModifyINIBool( bRead,_T("Email"),_T("SendEmailOnAdd"), FALSE, &m_Email.bSendEmailOnAdd );

	// [UserInfo]
	ModifyINIString( bRead,_T("UserInfo"),_T("UserName"),_T(""), &m_UserInfo.szUserName );
	ModifyINIString( bRead,_T("UserInfo"),_T("Password"),_T(""), &m_UserInfo.szPassword );
	ModifyINIString( bRead,_T("UserInfo"),_T("Domain"),_T(""), &m_UserInfo.szDomain );

	// [FTPWEB]
	ModifyINIInt( bRead,_T("FTPWEB"),_T("ServerPort"), 21, &m_FTP_WEB.nServerPort );
	ModifyINIString( bRead,_T("FTPWEB"),_T("ServerName"),_T(""), &m_FTP_WEB.szServerName );
	ModifyINIString( bRead,_T("FTPWEB"),_T("FTPUID"),_T(""), &m_FTP_WEB.szFTPUID );
	ModifyINIString( bRead,_T("FTPWEB"),_T("FTPPWD"),_T(""), &m_FTP_WEB.szFTPPWD );	
	ModifyINIString( bRead,_T("FTPWEB"),_T("ServerFolder"),_T(""), &m_FTP_WEB.szServerFolder );
	ModifyINIBool( bRead,_T("FTPWEB"),_T("ShowStatus"), TRUE, &m_FTP_WEB.bShowStatus );

	// [FTPILL]
	ModifyINIInt( bRead,_T("FTPILL"),_T("ServerPort"), 21, &m_FTP_ILL.nServerPort );
	ModifyINIString( bRead,_T("FTPILL"),_T("ServerName"),_T(""), &m_FTP_ILL.szServerName );
	ModifyINIString( bRead,_T("FTPILL"),_T("FTPUID"),_T(""), &m_FTP_ILL.szFTPUID );
	ModifyINIString( bRead,_T("FTPILL"),_T("FTPPWD"),_T(""), &m_FTP_ILL.szFTPPWD );	
	ModifyINIString( bRead,_T("FTPILL"),_T("ServerFolder"),_T(""), &m_FTP_ILL.szServerFolder );
	ModifyINIBool( bRead,_T("FTPILL"),_T("ShowStatus"), TRUE, &m_FTP_ILL.bShowStatus );

	// correct paths remove trailing backslash
	if( m_General.szRootFolder.Right( 1 ) == _T("\\") )
		m_General.szRootFolder.Delete( m_General.szRootFolder.GetLength() - 1 );
	if( m_Queue.szQueueFolder.Right( 1 ) == _T("\\") )
		m_Queue.szQueueFolder.Delete( m_Queue.szQueueFolder.GetLength() - 1 );
}

BOOL CAppConfig::ShowLastError(DWORD error)
{
	if(error != NO_ERROR)
	{
		CString lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL );
		// Process any inserts in lpMsgBuf.
		// ...
		// Display the string.
		AfxMessageBox( lpMsgBuf, MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}
	return TRUE;
}

CString CAppConfig::GetFormattedDate(INT nProject)
{
	CTime time(CTime::GetCurrentTime());
	CTimeSpan span( nProject, 0, 0, 0 );
	CString szDate;

	time += span;
	szDate = time.Format( m_General.szAdvertiseDateFormat );

	return szDate;
}

CString CAppConfig::GetFormattedDateTime(BOOL bDateOnly, BOOL bTabbed, BOOL bNonFile)
{
	CTime time(CTime::GetCurrentTime());
	TCHAR timeBuf[20+1];
	TCHAR dateBuf[20+1];
	SYSTEMTIME sysTime;
	INT bytesRead;
	CString string;

	// get system date/time formatted
	time.GetAsSystemTime(sysTime);
	VERIFY( bytesRead = GetTimeFormat( LOCALE_USER_DEFAULT, NULL,  &sysTime, NULL, timeBuf, 20 ));
	VERIFY( GetDateFormat( LOCALE_USER_DEFAULT, NULL,  &sysTime, NULL, dateBuf, 20 ));

	if( !bytesRead )
		string =_T("??:??");
	else
	{
		if( bTabbed )
			string.Format(_T("%s\t%s"), dateBuf, timeBuf );
		else if( bNonFile )
			string.Format(_T("%s %s"), dateBuf, timeBuf );
		else
		{
			// replace bad file type characters
			CString csDate = dateBuf;
			CString csTime = timeBuf;
			csDate.Replace( '\\', '-' );
			csDate.Replace( '/', '-' );
			csTime.Replace( ':', '-' );
			// format string
			if( bDateOnly )
				string = csDate;
			else
				string.Format(_T("%s %s"), csDate, csTime );
		}
	}

	return string;
}

BOOL CAppConfig::FormatMacros(CString* pszString, LONG nItemID, LONG nCurrentStaffID, LONG nNewStatusID, LONG nCurrentLibraryID, LONG nUserID, CFilePair* pFiles)
{
	LONG nStaffID = -1;
	LONG nLibraryID = -1;
	LONG nStatusID = -1;
	CString szTemp;
	CString szLocation;

	// current staff
	if( nCurrentStaffID > 0 )
	{
		// convert staff ID
		TCHAR cBuffer[64];
		memset(cBuffer, NULL, 64);
		_ltot_s( nCurrentStaffID, cBuffer, 10 );

		//staff description
		STAFF_LOOKUP Staff;
		m_SQL.GetDescriptionStaff( &Staff, nCurrentStaffID );

		// replace strings
		pszString->Replace( _T("(CURRENT_STAFF_ID)"), cBuffer );
		pszString->Replace( _T("(CURRENT_STAFF_EMAIL)"), Staff.szStaffEmail );
		pszString->Replace( _T("(CURRENT_STAFF_NAME)"), Staff.szStaffName );
		pszString->Replace( _T("(CURRENT_STAFF_PHONE)"), Staff.szStaffPhone );
	}

	// new proposed status
	if( nNewStatusID >= 0 )
	{
		// get description_status
		STATUS_LOOKUP Status;
		m_SQL.GetDescriptionStatus( &Status, nNewStatusID );
		// replace
		pszString->Replace( _T("(STATUS)"), Status.szDescription );
		pszString->Replace( _T("(STATUS_EXPLANATION)"), Status.szExplanation );
		pszString->Replace( _T("(STATUS_SUBJECT)"), Status.szSubject );
		pszString->Replace( _T("(STATUS_MESSAGE)"), Status.szMessage );
		// retained from v2.0
		pszString->Replace( _T("(INFORMATION)"), Status.szDescription );
		pszString->Replace( _T("(STATUS_CODE)"), Status.szCode );
	}

	// check for request
	if( nItemID > 0 )
	{
		// get request
		REQUEST_SET Request;
		m_SQL.GetRequestSet( &Request, nItemID );

		// get ids
		nUserID = Request.m_user_id;
		nStaffID = Request.m_staff_id;
		nLibraryID = Request.m_library_id;
		nStatusID = Request.m_status_id;
		// replace strings
		pszString->Replace( _T("(REQUEST)"), Request.m_request );
		szTemp.Format( _T("%.8ld"), Request.m_id );
		pszString->Replace( _T("(REQUESTID)"), szTemp );
		szTemp.Format( _T("%d"), Request.m_redirected );
		pszString->Replace( _T("(REDIRECTED)"), szTemp );
		pszString->Replace( _T("(PROJECT)"), Request.m_project );
		pszString->Replace( _T("(REQUESTFILE)"), Request.m_file );
		pszString->Replace( _T("(REQUESTINSTRUCTIONS)"), Request.m_instructions );
		pszString->Replace( _T("(REQUESTINFORMATION)"), Request.m_information );
		pszString->Replace( _T("(REQUESTNOTES)"), Request.m_notes );
		pszString->Replace( _T("(REQUESTUID)"), Request.m_uid );
		pszString->Replace( _T("(REQUESTSOURCE)"), Request.m_source );
		// date
		szTemp.Format( _T("%s"), Request.m_date_in.Format( m_General.szDateFormat ) );
		pszString->Replace( _T("(REQUESTDATE)"), szTemp );

		// get description_status
		STATUS_LOOKUP Status;
		m_SQL.GetDescriptionStatus( &Status, Request.m_status_id );
		// replace
		pszString->Replace( _T("(STATUS)"), Status.szDescription );
		pszString->Replace( _T("(STATUS_EXPLANATION)"), Status.szExplanation );
		pszString->Replace( _T("(STATUS_SUBJECT)"), Status.szSubject );
		pszString->Replace( _T("(STATUS_MESSAGE)"), Status.szMessage );
		pszString->Replace( _T("(STATUS_CODE)"), Status.szCode );

		// status filled not filled
		if( Request.m_status_id == STATUS_ID_NEW )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );
		else if( Request.m_status_id == STATUS_ID_PENDING )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );
		else if( Request.m_status_id == STATUS_ID_IN_PROCESS )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );
		else if( Request.m_status_id >= STATUS_OFFSET_HOLD && Request.m_status_id < STATUS_OFFSET_HOLD + 100 )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );
		else if( Request.m_status_id >= STATUS_OFFSET_ILL && Request.m_status_id < STATUS_OFFSET_ILL + 100 )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );
		else if( Request.m_status_id >= STATUS_OFFSET_PROBLEM && Request.m_status_id < STATUS_OFFSET_PROBLEM + 100 )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );
		else if( Request.m_status_id >= STATUS_OFFSET_NA && Request.m_status_id < STATUS_OFFSET_NA + 100 )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Not Filled") );
		else if( Request.m_status_id == STATUS_ID_COMPLETE )
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Filled") );
		else
			pszString->Replace( _T("(STATUS_FILLED)"), _T("Status") );

		// get description_priority
		PRIORITY_LOOKUP Priority;
		m_SQL.GetDescriptionPriority( &Priority, Request.m_priority_id );
		// replace
		pszString->Replace( _T("(PRIORITY)"), Priority.szDescription );

		//description_method
		METHOD_LOOKUP Method;
		m_SQL.GetDescriptionMethod( &Method, Request.m_method_id );
		// replace
		pszString->Replace( _T("(METHOD)"), Method.szDescription );
		pszString->Replace( _T("(METHOD_PRINTNOTICE)"), Method.szPrintNotice );
		pszString->Replace( _T("(METHOD_INFORMATION)"), Method.szInformation );

		CString szDescription;

		//description_ill
		ILL_LOOKUP ILL;
		m_SQL.GetDescriptionILL( &ILL, Request.m_ill_id );
		pszString->Replace( _T("(ILL)"), ILL.szDescription );
		pszString->Replace( _T("(ILL_SUBJECT)"), ILL.szMessageSubject );

		//description_referrals
		szDescription = m_SQL.GetDescription( CMySQLEx::referrals, Request.m_referrals );
		pszString->Replace( _T("(REFERRALS)"), szDescription );

		//description_copyright
		szDescription = m_SQL.GetDescription( CMySQLEx::copyright, Request.m_copyright );
		pszString->Replace( _T("(COPYRIGHT)"), szDescription );

		//description_collection
		COLLECTION_LOOKUP Collection;
		m_SQL.GetDescriptionCollection( &Collection, Request.m_collection_id );
		// replace
		pszString->Replace( _T("(COLLECTION)"), Collection.szDescription );
		pszString->Replace( _T("(COLLECTIONNAME)"), Collection.szName );
		pszString->Replace( _T("(COLLECTION_PRINTNOTICE)"), Collection.szPrintNotice );

		// alternate location
		if( !Request.m_location.IsEmpty() )
			szLocation = Request.m_location;

		// link
		pszString->Replace( _T("(REQUEST_LINK)"), Request.m_link );
	}

	// check for valid library
	if( nLibraryID >= 0  )
	{
		//description_library
		LIBRARY_LOOKUP Library;
		m_SQL.GetDescriptionLibrary( &Library, nLibraryID );

		// replace
		pszString->Replace( _T("(LIBRARY)"), Library.szDescription );
		pszString->Replace( _T("(LIBRARYNAME)"), Library.szName );
		pszString->Replace( _T("(LIBRARYADDRESS)"), Library.szAddress );
		pszString->Replace( _T("(LIBRARYEMAIL)"), Library.szEmail );
		pszString->Replace( _T("(LIBRARYPHONE)"), Library.szPhone );
		pszString->Replace( _T("(LIBRARY_INFORMATION)"), Library.szInformation );
	}

	// current library
	if( nCurrentLibraryID >= 0 )
	{
		//description_library
		LIBRARY_LOOKUP Library;
		m_SQL.GetDescriptionLibrary( &Library, nCurrentLibraryID );

		// replace strings
		pszString->Replace( _T("(CURRENT_LIBRARY)"), Library.szDescription );
		pszString->Replace( _T("(CURRENT_LIBRARYNAME)"), Library.szName );
		pszString->Replace( _T("(CURRENT_LIBRARYADDRESS)"), Library.szAddress );
		pszString->Replace( _T("(CURRENT_LIBRARYEMAIL)"), Library.szEmail );
		pszString->Replace( _T("(CURRENT_LIBRARYPHONE)"), Library.szPhone );
		pszString->Replace( _T("(CURRENT_LIBRARY_INFORMATION)"), Library.szInformation );
	}

	// check for valid staff
	if( nStaffID > 0 )
	{
		//staff description
		STAFF_LOOKUP Staff;
		m_SQL.GetDescriptionStaff( &Staff, nStaffID );

		// replace strings
		pszString->Replace( _T("(WORKERLINK)"), m_General.szWorkerLink );
		pszString->Replace( _T("(STAFFEMAIL)"), Staff.szStaffEmail );
		pszString->Replace( _T("(STAFFNAME)"), Staff.szStaffName );
	}
	else
	{
		pszString->Replace( _T("(WORKERLINK)"), m_General.szWorkerLink );
		pszString->Replace( _T("(STAFFEMAIL)"), _T("") );
		pszString->Replace( _T("(STAFFNAME)"), _T("") );
	}

	// check for valid user
	if( nUserID > 0 )
	{
		//staff description
		USER_SET User;
		m_SQL.GetUserSet( &User, nUserID );

		// get user type info
		USER_TYPE_LOOKUP UserType;
		m_SQL.GetDescriptionUserType( &UserType, User.m_type );

		// replace strings
		szTemp.Format( _T("%ld"), User.m_id );
		pszString->Replace( _T("(USERID)"), szTemp );
		pszString->Replace( _T("(USERPID)"), User.m_pwd );
		pszString->Replace( _T("(USERELANID)"), User.m_lan_id );
		pszString->Replace( _T("(USERNAME)"), User.m_name );
		pszString->Replace( _T("(USEREMAIL)"), User.m_email );
		pszString->Replace( _T("(USERPHONE)"), User.m_phone );
		pszString->Replace( _T("(USERFAX)"), User.m_fax );
		pszString->Replace( _T("(USERCOMPANY)"), User.m_company );

		pszString->Replace( _T("(USERSHIPTO)"), User.m_shipto );
		pszString->Replace( _T("(USERADDRESS1)"), User.m_address1 );
		pszString->Replace( _T("(USERADDRESS2)"), User.m_address2 );
		pszString->Replace( _T("(USERCITY)"), User.m_city );
		pszString->Replace( _T("(USERSTATE)"), User.m_state );
		pszString->Replace( _T("(USERZIPCODE)"), User.m_zipcode );
		pszString->Replace( _T("(USERCOUNTRY)"), User.m_country );
		pszString->Replace( _T("(USERBILLTO)"), User.m_billto );
		pszString->Replace( _T("(USERBILLADDRESS1)"), User.m_billaddress1 );
		pszString->Replace( _T("(USERBILLADDRESS2)"), User.m_billaddress2 );
		pszString->Replace( _T("(USERBILLCITY)"), User.m_billcity );
		pszString->Replace( _T("(USERBILLSTATE)"), User.m_billstate );
		pszString->Replace( _T("(USERBILLZIPCODE)"), User.m_billzipcode );
		pszString->Replace( _T("(USERBILLCOUNTRY)"), User.m_billcountry );

		// alternate location
		if( szLocation.IsEmpty() )
		{
			if( m_Options.bUseComapnyForLocation )
				szLocation = User.m_company;
			else
				szLocation = User.m_address1;
		}
		// set delivery location
		pszString->Replace( _T("(DELIVERY_LOCATION)"), szLocation );

		// replace links
		if( UserType.nExternal )
		{
			szTemp = m_External.szWEBLink;
			szTemp.Replace( _T(" "), _T("%20") );
			pszString->Replace( _T("(WEBLINK)"), szTemp );

			szTemp = m_External.szWEBNALink;
			szTemp.Replace( _T(" "), _T("%20") );
			pszString->Replace( _T("(WEBNALINK)"), szTemp );
		}
		else
		{
			szTemp = m_General.szWEBLink;
			szTemp.Replace( _T(" "), _T("%20") );
			pszString->Replace( _T("(WEBLINK)"), szTemp );

			szTemp = m_General.szWEBNALink;
			szTemp.Replace( _T(" "), _T("%20") );
			pszString->Replace( _T("(WEBNALINK)"), szTemp );
		}
	}

	// replace FTP link
	szTemp = m_General.szFTPLink;
	szTemp.Replace( _T(" "), _T("%20") );
	pszString->Replace( _T("(FTPLINK)"), szTemp );

	// set file name
	if( pFiles )
	{
		for( INT nCount = 0; nCount < pFiles->GetCount(); nCount++ )
		{
			pszString->Replace( _T("(FILENAME)"), pFiles->GetAt( nCount ).szFileTo );
		}
	}

	// multiple files
	if( pFiles && pFiles->GetCount() > 1 )
	{
		pszString->Replace( _T("(FILES)"), _T("files") );
		pszString->Replace( _T("(ARTC)"), _T("articles") );
		pszString->Replace( _T("(ARIS)"), _T("are") );
		pszString->Replace( _T("(HASV)"), _T("have") );
		pszString->Replace( _T("(ITEMS)"), _T("items") );
		pszString->Replace( _T("(ITEMS_C)"), _T("Items") );
		pszString->Replace( _T("(MATERIALS)"), _T("materials") );
		pszString->Replace( _T("(MATERIALS_C)"), _T("Materials") );
	}
	else
	{
		pszString->Replace( _T("(FILES)"), _T("file") );
		pszString->Replace( _T("(ARTC)"), _T("article") );
		pszString->Replace( _T("(ARIS)"), _T("is") );
		pszString->Replace( _T("(HASV)"), _T("has") );
		pszString->Replace( _T("(ITEMS)"), _T("item") );
		pszString->Replace( _T("(ITEMS_C)"), _T("Item") );
		pszString->Replace( _T("(MATERIALS)"), _T("material") );
		pszString->Replace( _T("(MATERIALS_C)"), _T("Materials") );
	}

	// set date
	pszString->Replace( _T("(DATE)"), GetFormattedDate() );
	pszString->Replace( _T("(DDAY)"), GetFormattedDate( m_General.nAdvertiseDaysOld ) );

	// information
	BOOL UserList = FALSE;
	m_SQL.GetInformation( INFORMATION_NEWS_ITEM_MACRO, &szTemp, &UserList );
	if( UserList )
		pszString->Replace( _T("(NEWS_ITEM)"), szTemp );
	else
		pszString->Replace( _T("(NEWS_ITEM)"), _T("") );

	// return
	return TRUE;
}

LONG CAppConfig::GetIDFromString(CString szString)
{
	return _tstol( szString.Left( szString.Find( _T(" ") )));
}

LONG CAppConfig::FindStringArrayID(CStringArray* pStringArray, CString szString, CString szType)
{
	LONG nID = -1;

	// loop through array
	for( INT nIndex = 0; nIndex < pStringArray->GetCount(); nIndex++ )
	{
		if( szString == pStringArray->GetAt( nIndex ) )
		{
			nID = nIndex;
			break;
		}
	}
	// not found message
	if( nID < 0 )
	{
		CString szMessage;
		szMessage.Format( _T("ERROR:\n\n%s \"%s\" was not found, please re-enter."), szType, szString );
		AfxMessageBox( szMessage );
	}
	//return ID
	return nID;
}

COLORREF CAppConfig::GetColorRef(CString szColor)
{
	COLORREF crColor = 0;
	DWORD dwR = 0;
	DWORD dwG = 0;
	DWORD dwB = 0;

	// strip leading #
	szColor.Remove( '#' );
	// trim
	szColor.Trim();

	// get color
	dwR = _tcstoul( szColor.Mid( 0, 2 ), NULL, 16 );
	dwG = _tcstoul( szColor.Mid( 2, 2 ), NULL, 16 );
	dwB = _tcstoul( szColor.Mid( 4, 2 ), NULL, 16 );
	// set color
	crColor = RGB( dwR, dwG, dwB );
	// return color ref
	return crColor;
}

COLORREF CAppConfig::DarkenColor(COLORREF clrColor, DOUBLE darken)
{
	if (darken >= 0.0 && darken < 1.0)
	{
		BYTE color_r, color_g, color_b;
		color_r = (BYTE)(GetRValue(clrColor) * darken);
		color_g = (BYTE)(GetGValue(clrColor) * darken);
		color_b = (BYTE)(GetBValue(clrColor) * darken);
		clrColor = RGB(color_r, color_g, color_b);
	}
	
	return clrColor;
}

COLORREF CAppConfig::LightenColor(COLORREF clrColor, DOUBLE lighten)
{
	if (lighten > 0.0 && lighten <= 1.0)
	{
		BYTE color_r, color_g, color_b;
		
		lighten += 1.0;
		color_r = (BYTE)min((DWORD)GetRValue(clrColor) * lighten, 255.0);
		color_g = (BYTE)min((DWORD)GetGValue(clrColor) * lighten, 255.0);
		color_b = (BYTE)min((DWORD)GetBValue(clrColor) * lighten, 255.0);
		clrColor = RGB(color_r, color_g, color_b);
	}
	
	return clrColor;
}

UINT CAppConfig::Convert2Ascii(UINT nChar)
{
	if( nChar < 0x020 ) return UINT(' ');

	if( nChar == 0xA0 ) return UINT(' ');
	if( nChar == 0xA1 ) return UINT(' ');
	if( nChar == 0xA2 ) return UINT(' ');
	if( nChar == 0xA3 ) return UINT(' ');
	if( nChar == 0xA4 ) return UINT(' ');
	if( nChar == 0xA5 ) return UINT(' ');
	if( nChar == 0xA6 ) return UINT(' ');
	if( nChar == 0xA7 ) return UINT(' ');
	if( nChar == 0xA8 ) return UINT(' ');
	if( nChar == 0xA9 ) return UINT(' ');
	if( nChar == 0xAA ) return UINT(' ');
	if( nChar == 0xAB ) return UINT(' ');
	if( nChar == 0xAC ) return UINT(' ');
	if( nChar == 0xAD ) return UINT(' ');
	if( nChar == 0xAE ) return UINT(' ');
	if( nChar == 0xAF ) return UINT(' ');

	if( nChar == 0xB0 ) return UINT(' ');
	if( nChar == 0xB1 ) return UINT(' ');
	if( nChar == 0xB2 ) return UINT(' ');
	if( nChar == 0xB3 ) return UINT(' ');
	if( nChar == 0xB4 ) return UINT(' ');
	if( nChar == 0xB5 ) return UINT(' ');
	if( nChar == 0xB6 ) return UINT(' ');
	if( nChar == 0xB7 ) return UINT(' ');
	if( nChar == 0xB8 ) return UINT(' ');
	if( nChar == 0xB9 ) return UINT(' ');
	if( nChar == 0xBA ) return UINT(' ');
	if( nChar == 0xBB ) return UINT(' ');
	if( nChar == 0xBC ) return UINT(' ');
	if( nChar == 0xBD ) return UINT(' ');
	if( nChar == 0xBE ) return UINT(' ');
	if( nChar == 0xBF ) return UINT(' ');

	if( nChar == 0xC0 ) return UINT('A');
	if( nChar == 0xC1 ) return UINT('A');
	if( nChar == 0xC2 ) return UINT('A');
	if( nChar == 0xC3 ) return UINT('A');
	if( nChar == 0xC4 ) return UINT('A');
	if( nChar == 0xC5 ) return UINT('A');
	if( nChar == 0xC6 ) return UINT('A');
	if( nChar == 0xC7 ) return UINT('C');
	if( nChar == 0xC8 ) return UINT('E');
	if( nChar == 0xC9 ) return UINT('E');
	if( nChar == 0xCA ) return UINT('E');
	if( nChar == 0xCB ) return UINT('E');
	if( nChar == 0xCC ) return UINT('I');
	if( nChar == 0xCD ) return UINT('I');
	if( nChar == 0xCE ) return UINT('I');
	if( nChar == 0xCF ) return UINT('I');

	if( nChar == 0xD0 ) return UINT('D');
	if( nChar == 0xD1 ) return UINT('N');
	if( nChar == 0xD2 ) return UINT('O');
	if( nChar == 0xD3 ) return UINT('O');
	if( nChar == 0xD4 ) return UINT('O');
	if( nChar == 0xD5 ) return UINT('O');
	if( nChar == 0xD6 ) return UINT('O');
	if( nChar == 0xD7 ) return UINT('x');
	if( nChar == 0xD8 ) return UINT('O');
	if( nChar == 0xD9 ) return UINT('U');
	if( nChar == 0xDA ) return UINT('U');
	if( nChar == 0xDB ) return UINT('U');
	if( nChar == 0xDC ) return UINT('U');
	if( nChar == 0xDD ) return UINT('Y');
	if( nChar == 0xDE ) return UINT('D');
	if( nChar == 0xDF ) return UINT('B');

	if( nChar == 0xE0 ) return UINT('a');
	if( nChar == 0xE1 ) return UINT('a');
	if( nChar == 0xE2 ) return UINT('a');
	if( nChar == 0xE3 ) return UINT('a');
	if( nChar == 0xE4 ) return UINT('a');
	if( nChar == 0xE5 ) return UINT('a');
	if( nChar == 0xE6 ) return UINT('a');
	if( nChar == 0xE7 ) return UINT('c');
	if( nChar == 0xE8 ) return UINT('e');
	if( nChar == 0xE9 ) return UINT('e');
	if( nChar == 0xEA ) return UINT('e');
	if( nChar == 0xEB ) return UINT('e');
	if( nChar == 0xEC ) return UINT('i');
	if( nChar == 0xED ) return UINT('i');
	if( nChar == 0xEE ) return UINT('i');
	if( nChar == 0xEF ) return UINT('i');

	if( nChar == 0xF0 ) return UINT('o');
	if( nChar == 0xF1 ) return UINT('n');
	if( nChar == 0xF2 ) return UINT('o');
	if( nChar == 0xF3 ) return UINT('o');
	if( nChar == 0xF4 ) return UINT('o');
	if( nChar == 0xF5 ) return UINT('o');
	if( nChar == 0xF6 ) return UINT('o');
	if( nChar == 0xF7 ) return UINT('-');
	if( nChar == 0xF8 ) return UINT('o');
	if( nChar == 0xF9 ) return UINT('u');
	if( nChar == 0xFA ) return UINT('u');
	if( nChar == 0xFB ) return UINT('u');
	if( nChar == 0xFC ) return UINT('u');
	if( nChar == 0xFD ) return UINT('y');
	if( nChar == 0xFE ) return UINT('p');
	if( nChar == 0xFF ) return UINT('y');

	if( nChar == 0x7E ) return UINT(' ');

	return 0;
}

BOOL CAppConfig::NormalizeString(CString* pszString)
{
	// Use this for now with check online
	// Needs to be updated to include URI decode functionality
	pszString->Replace( _T("&amp;"), _T("&") );	
	return TRUE;
}

BOOL CAppConfig::CorrectCitation(CString* pszCitation)
{
	pszCitation->Replace( _T(":\t\r\n"), _T(": ") );
	return TRUE;
}

CString CAppConfig::QuoteString(CString szString)
{
	CString szNew = szString;

	szNew.Replace(_T("\""), _T("\"\""));
	
	return szNew;
}

BOOL CAppConfig::Convert2Multibyte(CString* pszSource, char* pszDest, INT nDestLen)
{
	// copy
	for( INT nIndex = 0; nIndex < nDestLen; nIndex++ )
	{
		if( nIndex < pszSource->GetLength() )
			pszDest[nIndex] = (INT)pszSource->GetAt( nIndex );
		else
			pszDest[nIndex] = '\0';
	}
	return TRUE;
}

INT CAppConfig::SplitString(const CString& strInputString, const CString& strDelimiter, CStringArray& arrStringArray)
{
	INT iPos = 0;
	INT newPos = -1;
	INT sizeS2 = strDelimiter.GetLength();
	INT isize = strInputString.GetLength();

	CArray<INT, INT> positions;

	newPos = strInputString.Find (strDelimiter, 0);

	if( newPos < 0 )
	{
		if( strInputString.IsEmpty() )
			return 0;
		// add string
		arrStringArray.Add( strInputString );
		return 1;
	}

	INT numFound = 0;

	while( newPos > iPos )
	{
		numFound++;
		positions.Add(newPos);
		iPos = newPos;
		newPos = strInputString.Find (strDelimiter, iPos + sizeS2);
	}

	for( INT i=0; i <= positions.GetSize(); i++ )
	{
		CString s;
		if( i == 0 )
			s = strInputString.Mid( i, positions[i] );
		else
		{
			INT offset = positions[i-1] + sizeS2;
			if( offset < isize )
			{
				if( i == positions.GetSize() )
					s = strInputString.Mid(offset);
				else if( i > 0 )
					s = strInputString.Mid( positions[i-1] + sizeS2, positions[i] - positions[i-1] - sizeS2 );
			}
		}
		arrStringArray.Add(s);
	}
	return numFound;
}

BOOL CAppConfig::MapDrive(LPCTSTR szPath, BOOL bConnect, LPCTSTR szDomain, LPCTSTR szUser, LPCTSTR szPass)
{
	CWaitCursor wait;
	TCHAR szUserName[MAX_PATH];	
	DWORD res;

	CString szFolder = szPath;
	if( szFolder.Right( 1 ) ==_T("\\") )
		szFolder.Delete( szFolder.GetLength() - 1 );

	// set domain
	if( _tcslen( szDomain ) > 0 )
		_stprintf_s( szUserName, _T("%s\\%s"), szDomain, szUser );
	
	if( bConnect )
	{
		NETRESOURCE nr;
		nr.dwScope = RESOURCE_GLOBALNET;
		nr.dwType = RESOURCETYPE_DISK;
		nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
		nr.lpLocalName = NULL;;
		nr.lpRemoteName = (LPTSTR)(LPCTSTR)szFolder;
		nr.lpProvider = NULL;

		// first disconnect current connections
		res = WNetCancelConnection2( szFolder, CONNECT_UPDATE_PROFILE, TRUE );
		if( res == NO_ERROR || res == ERROR_NOT_CONNECTED )
			res = WNetAddConnection2( &nr, szPass, szUserName, CONNECT_INTERACTIVE );
	}
	else
		res = WNetCancelConnection2( szFolder, CONNECT_UPDATE_PROFILE, FALSE );

	//
	// If the call succeeds, inform the user; otherwise,
	//  print the error.
	//
	if(res != NO_ERROR)
	{
		CString lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL );
		// Process any inserts in lpMsgBuf.
		// ...
		// Display the string.
		AfxMessageBox( lpMsgBuf, MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	return TRUE;
}

BOOL CAppConfig::WriteWindowLocation(CString szWindow, CRect rect)
{
	// write settings
	AfxGetApp()->WriteProfileBinary( _T("Settings"), szWindow, (LPBYTE)&rect, sizeof(CRect) );
	return TRUE;
}

BOOL CAppConfig::ReadWindowLocation(CString szWindow, CRect* prect)
{
	UINT nLen = 0;
	LPBYTE pb = NULL;

	// get settings
	if( AfxGetApp()->GetProfileBinary( _T("Settings"), szWindow, &pb, &nLen ))
    {
        ASSERT( nLen == sizeof(CRect) );
        memcpy( prect, pb, sizeof(CRect) );
        delete pb;
    }
	return TRUE;
}

BOOL CAppConfig::UpdateRequestHistory(LONG nItemID, LONG nUserID, LONG nStatus, CString szText)
{
	// check user id
	if( nItemID < 0 || nUserID < 0 )
	{
		AfxMessageBox( _T("Error requset history ID not identified.") );
		return FALSE;
	}

	return m_SQL.UpdateRequestHistory( nItemID, nUserID, nStatus, szText );
}

BOOL CAppConfig::UpdateUserHistory(LONG nUserID, CString szText)
{
	// check user id
	if( nUserID < 0 )
	{
		AfxMessageBox( _T("Error user not identified.") );
		return FALSE;
	}

	return m_SQL.UpdateUserHistory( nUserID, szText );
}

void CAppConfig::WriteToLog(CString filename, CString text, CString token, CString number, CString title)
{
	// Open the file with the specified encoding
	FILE *fStream;
	errno_t eOpen = FALSE;
	BOOL bQuit = FALSE;
	INT iCount = 0;
	CString date;
	TCHAR name[64];

	// get date
	date = GetFormattedDateTime();
	// get name
	memset( name, NULL, 64);
	GetEnvironmentVariable( m_General.szEnvironmentVariable, name, 64);
	// format line
	CString string;
	string.Format(_T("%s\t%s\t%s\t%s\t%s\t%s\n"), name, date, token, number, text, title);

	while( !bQuit )
	{
		// open file
		eOpen = _tfopen_s(&fStream, filename, _T("a+t,ccs=UTF-8"));
		// not open?
		if( eOpen != 0 )
			iCount++;
		// open so procede
		else
			bQuit = TRUE;
		// check 20 times
		if( iCount > 20 )
			bQuit = TRUE;
	}
	// open the file from this stream
	CStdioFile file(fStream);  
	if( eOpen == 0 )
	{	
		try
		{			
			file.SeekToEnd();		
			file.WriteString(string);
			file.Close();		
		}
		catch (CFileException* e)
		{
			e->ReportError();
			e->Delete();
		}
	}
}

void CAppConfig::WriteToFile(CString filename, CString text, BOOL bRemoveReturns)
{
	// Open the file with the specified encoding
	FILE *fStream;
	errno_t eOpen = FALSE;
	BOOL bQuit = FALSE;
	INT iCount = 0;

	// attempt to open for appending using UTF-8
	while( !bQuit )
	{
		// open file
		eOpen = _tfopen_s(&fStream, filename, _T("a+t,ccs=UTF-8"));
		// not open?
		if( eOpen != 0 )
			iCount++;
		// open so procede
		else
			bQuit = TRUE;
		// check 20 times
		if( iCount > 20 )
			bQuit = TRUE;
	}
	// open the file from this stream
	CStdioFile file(fStream);  
	if( eOpen == 0 )
	{
		// remove \r\n
		if( bRemoveReturns )
		{
			text.Replace( _T("\n"), _T(" ") );
			text.Replace( _T("\r"), _T(" ") );
		}
		try
		{			
			file.SeekToEnd();		
			file.WriteString(text);
			file.Close();		
		}
		catch (CFileException* e)
		{
			e->ReportError();
			e->Delete();
		}
	}
}

void CAppConfig::EnCrypt(TCHAR* inp, TCHAR* key)
{
	//we will consider size of sbox 256 bytes
	//(extra byte are only to prevent any mishep just in case)
	TCHAR Sbox[257], Sbox2[257];
	ULONG i = 0, j = 0, t = 0, x = 0;

	// get length of inp
	size_t inplen = _tcslen( inp );
	size_t keylen = _tcslen( key );

	//this unsecured key is to be used only when there is no input key from user
	TCHAR OurUnSecuredKey[] = _T( "TeamSoftwareSolutions" );
	size_t OurKeyLen = _tcslen( OurUnSecuredKey );
	TCHAR temp = 0, k = 0;

	//always initialize the arrays with zero
	ZeroMemory( Sbox, sizeof( Sbox ));
	ZeroMemory( Sbox2, sizeof( Sbox2 ));

	//initialize sbox i
	for( i = 0; i < 256U; i++ )
	{
		Sbox[i] = ( TCHAR )i;
	}

	j = 0;
	//whether user has sent any inpur key
	if( keylen )
	{
		//initialize the sbox2 with user key
		for( i = 0; i < 256U ; i++ )
		{
			if( j == keylen )
			{
				j = 0;
			}
			Sbox2[i] = key[j++];
		}
	}
	else
	{
		//initialize the sbox2 with our key
		for( i = 0; i < 256U ; i++ )
		{
			if( j == OurKeyLen )
			{
				j = 0;
			}
			Sbox2[i] = OurUnSecuredKey[j++];
		}
	}

	j = 0 ; //Initialize j
	//scramble sbox1 with sbox2
	for( i = 0; i < 256; i++ )
	{
		j = ( j + ( unsigned long ) Sbox[i] + ( unsigned long ) Sbox2[i] ) % 256U ;
		temp =  Sbox[i];                    
		Sbox[i] = Sbox[j];
		Sbox[j] =  temp;
	}

	i = j = 0;
	for( x = 0; x < inplen; x++ )
	{
		//increment i
		i = (i + 1U) % 256U;
		//increment j
		j = (j + ( unsigned long ) Sbox[i] ) % 256U;

		//Scramble SBox #1 further so encryption routine will
		//will repeat itself at great interval
		temp = Sbox[i];
		Sbox[i] = Sbox[j] ;
		Sbox[j] = temp;

		//Get ready to create pseudo random  byte for encryption key
		t = ( ( unsigned long ) Sbox[i] + ( unsigned long ) Sbox[j] ) %  256U ;

		//get the random byte
		k = Sbox[t];

		//xor with the data and done
		//inp[x] = ( inp[x] ^ k );
		if ((inp[x] ^ k) == _T('\x0000'))
			inp[x] = inp[x];
		else if ((inp[x] ^ k) == _T('\n'))
			inp[x] = inp[x];
		else
			inp[x] = (inp[x] ^ k);
	}
}

BOOL CAppConfig::Hash(CString* pszString)
{
	char cPWD[128];	
	INT nPWDLen = 0;
	CString szHash;

	// convert for hash and get length
	Convert2Multibyte( pszString, cPWD, sizeof(cPWD) );
	nPWDLen = strlen(cPWD);
	if( nPWDLen == 0 )
		return FALSE;

	// check for sha, default to md5
	if( m_General.szPasswordHash.CompareNoCase( _T("sha256") ) == 0 || m_General.szPasswordHash.CompareNoCase( _T("sha512") ) == 0 )
	{
		// set string hash length
		int nLength = 256;
		if( m_General.szPasswordHash.CompareNoCase( _T("sha512") ) == 0 )
			nLength = 512;

		sha2_ctx sha;
		int hashLength = nLength / 8;		
		unsigned char tempOut[256];

		// Hashing a string, open the object, give it the text, and finalise it
		sha2_begin( nLength, &sha );
		sha2_hash( reinterpret_cast<unsigned char *>(cPWD), nPWDLen, &sha );
		sha2_end( tempOut, &sha );
		for( int i = 0; i < hashLength; i++ )
		{
			char tmp[3];
			_itoa_s( tempOut[i], tmp, 3, 16);
			if( strlen(tmp) == 1 )
			{
				tmp[1] = tmp[0];
				tmp[0] = '0';
				tmp[2] = '\0';
			}
			szHash += tmp;		
		}
	}
	else
	{
		// get hash
		szHash = MD5String( cPWD );
	}

	// set hash
	*pszString = szHash;
	// retrun
	return TRUE;
}

HRESULT CAppConfig::GetHTMLBody(CExplorer_window* pBrowser, CString &szHTML)
{
	USES_CONVERSION;

	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hResult = S_OK;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();

	if( spDispDocument == NULL )
		return S_FALSE;

	CComQIPtr<IHTMLDocument2> pHTMLDoc = spDispDocument;

	if( pHTMLDoc != NULL )
	{
		IHTMLElement *lpBodyElm;

		pHTMLDoc->get_body( &lpBodyElm );
		ASSERT( lpBodyElm );

		// get the BODY
		BSTR bstr;
		hResult = lpBodyElm->get_innerHTML( &bstr );
		// get HTML
		CString szText( bstr );
		szHTML = szText;
		// release pointers
		lpBodyElm->Release();
	}
	return hResult;
}

HRESULT CAppConfig::GetHTML(CExplorer_window* pBrowser, CString &szHTML)
{
	USES_CONVERSION;

	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hResult = S_OK;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();

	if( spDispDocument == NULL )
		return S_FALSE;

	CComQIPtr<IHTMLDocument2> pHTMLDoc = spDispDocument;

	if( pHTMLDoc != NULL )
	{
		IHTMLElement *lpBodyElm;
		IHTMLElement *lpParentElm;

		pHTMLDoc->get_body( &lpBodyElm );
		ASSERT( lpBodyElm );

		// the parent of BODY is HTML
		hResult = lpBodyElm->get_parentElement( &lpParentElm );
		ASSERT( lpParentElm );
		BSTR bstr;
		hResult = lpParentElm->get_outerHTML( &bstr );
		// get HTML
		CString szText( bstr );
		szHTML = szText;
		// release pointers
		lpParentElm->Release();
		lpBodyElm->Release();
	}
	return hResult;
}

HRESULT CAppConfig::SetHTML(CExplorer_window* pBrowser, CString szHTML)
{
	if( pBrowser == NULL )
		return S_FALSE;
	if( szHTML.IsEmpty() )
		return FALSE;

	HRESULT hResult = S_OK;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();

	if( spDispDocument == NULL )
		return S_FALSE;

	CComQIPtr<IHTMLDocument2> pHTMLDoc = spDispDocument;
	
	if( pHTMLDoc != NULL )
	{
		// construct text to be written to browser as SAFEARRAY
		SAFEARRAY *safe_array = SafeArrayCreateVector( VT_VARIANT, 0, 1 );
		VARIANT	*variant;
		SafeArrayAccessData( safe_array,(LPVOID *)&variant );
		variant->vt = VT_BSTR;
		variant->bstrVal = szHTML.AllocSysString();
		SafeArrayUnaccessData( safe_array );
		// write SAFEARRAY to browser document
		hResult = pHTMLDoc->write( safe_array );
	}
	return hResult;
}

HRESULT CAppConfig::ClearHTML(CExplorer_window* pBrowser)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hResult = S_OK;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();

	if( spDispDocument == NULL )
		return S_FALSE;

	CComQIPtr<IHTMLDocument2> pHTMLDoc = spDispDocument;
	
	if( pHTMLDoc != NULL )
	{
		pHTMLDoc->close();

		VARIANT		open_name;
		VARIANT		open_features;
		VARIANT		open_replace;
		IDispatch	*open_window = NULL;

		::VariantInit( &open_name );
		::VariantInit( &open_features );
		::VariantInit( &open_replace );

		open_name.vt = VT_BSTR;
		open_name.bstrVal = ::SysAllocString(L"_self");
		hResult = pHTMLDoc->open( ::SysAllocString(L"text/html"), open_name, open_features, open_replace, &open_window );
		if( open_window != NULL )
			open_window->Release();
	}
	return hResult;
}

HRESULT CAppConfig::SetEdit(CExplorer_window* pBrowser, BOOL bEdit)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hr = S_FALSE;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();
	CComQIPtr<IHTMLDocument2> m_pHTMLDoc = spDispDocument;
	
	// set to edit mode
	if( m_pHTMLDoc != NULL )
		hr = m_pHTMLDoc->put_designMode(CComBSTR(L"on"));
	// return
	return hr;
}

HRESULT CAppConfig::SetBold(CExplorer_window* pBrowser, BOOL bState)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hr = S_FALSE;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();
	CComQIPtr<IHTMLDocument2> m_pHTMLDoc = spDispDocument;
	IOleCommandTarget* pCmdTarg;
	m_pHTMLDoc->QueryInterface( IID_IOleCommandTarget, (void**)&pCmdTarg );

	if( pCmdTarg != NULL )
	{
		hr = pCmdTarg->Exec( &CGID_MSHTML, IDM_BOLD, MSOCMDEXECOPT_DODEFAULT, NULL, NULL );
	}
	return hr;
}

HRESULT CAppConfig::SetFontName(CExplorer_window* pBrowser)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hr = S_FALSE;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();
	CComQIPtr<IHTMLDocument2> m_pHTMLDoc = spDispDocument;
	IOleCommandTarget* pCmdTarg;
	m_pHTMLDoc->QueryInterface( IID_IOleCommandTarget, (void**)&pCmdTarg );

	if( pCmdTarg != NULL )
	{
		hr = pCmdTarg->Exec( &CGID_MSHTML, IDM_FONTNAME, MSOCMDEXECOPT_PROMPTUSER, NULL, NULL );
	}
	return hr;
}

HRESULT CAppConfig::SetFontSize(CExplorer_window* pBrowser, INT nSize)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hr = S_FALSE;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();
	CComQIPtr<IHTMLDocument2> m_pHTMLDoc = spDispDocument;
	IOleCommandTarget* pCmdTarg;
	m_pHTMLDoc->QueryInterface( IID_IOleCommandTarget, (void**)&pCmdTarg );

	if( pCmdTarg != NULL )
	{
		VARIANT vIn;
		V_VT(&vIn) = VT_I2; // Sets the type to INT
		V_I2(&vIn) = nSize;
		hr = pCmdTarg->Exec( &CGID_MSHTML, IDM_FONTSIZE, MSOCMDEXECOPT_PROMPTUSER, &vIn, NULL );
	}
	return hr;
}

HRESULT CAppConfig::SetItalic(CExplorer_window* pBrowser, BOOL bState)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hr = S_FALSE;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();
	CComQIPtr<IHTMLDocument2> m_pHTMLDoc = spDispDocument;
	IOleCommandTarget* pCmdTarg;
	m_pHTMLDoc->QueryInterface( IID_IOleCommandTarget, (void**)&pCmdTarg );
	
	if( pCmdTarg != NULL )
	{
		hr = pCmdTarg->Exec( &CGID_MSHTML, IDM_ITALIC, MSOCMDEXECOPT_DODEFAULT, NULL, NULL );
	}
	return hr;
}

HRESULT CAppConfig::SetColor(CExplorer_window* pBrowser, COLORREF cColor)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HRESULT hr = S_FALSE;
	CComPtr<IDispatch> spDispDocument;
	spDispDocument = pBrowser->get_Document();
	CComQIPtr<IHTMLDocument2> m_pHTMLDoc = spDispDocument;
	IOleCommandTarget* pCmdTarg;
	m_pHTMLDoc->QueryInterface( IID_IOleCommandTarget, (void**)&pCmdTarg );

	if( pCmdTarg != NULL )
	{
		VARIANT vIn;
		V_VT(&vIn) = VT_I4; // Sets the type to long
		V_I4(&vIn) = cColor; // NOTE: byte order is reversed
		hr = pCmdTarg->Exec(&CGID_MSHTML, IDM_FORECOLOR, MSOCMDEXECOPT_DODEFAULT, &vIn, NULL);
	}
	return S_FALSE;
}

HRESULT CAppConfig::LoadFromFolder(CExplorer_window* pBrowser, CString szResource)
{
	if( pBrowser == NULL )
		return S_FALSE;

	BOOL bRetVal = S_OK;

	// if no path try local
	if( szResource.Find( '\\' ) == -1 )
	{
		CString szPath;
		int nIndex = 0;
		TCHAR cBuffer[MAX_PATH];

		memset(cBuffer, NULL, MAX_PATH);
		GetModuleFileName(NULL, cBuffer, MAX_PATH);
		
		szPath = cBuffer;
		nIndex = szPath.ReverseFind('\\') + 1;
		szResource = szPath.Left( nIndex ) + szResource;
	}
	// navigate to file
	pBrowser->Navigate(szResource, NULL, NULL, NULL, NULL);

	return bRetVal;
}

HRESULT CAppConfig::LoadFromResource(CExplorer_window* pBrowser, UINT nRes)
{
	if( pBrowser == NULL )
		return S_FALSE;

	HINSTANCE hInstance = AfxGetResourceHandle();
	ASSERT(hInstance != NULL);

	CString strResourceURL;
	BOOL bRetVal = S_OK;
	LPTSTR lpszModule = new TCHAR[_MAX_PATH];

	if (GetModuleFileName(hInstance, lpszModule, _MAX_PATH))
	{
		strResourceURL.Format(_T("res://%s/%d"), lpszModule, nRes);
		pBrowser->Navigate(strResourceURL, NULL, NULL, NULL, NULL);
	}
	else
		bRetVal = S_FALSE;

	delete [] lpszModule;
	return bRetVal;
}

HRESULT CAppConfig::SetMargins(BOOL bReset)
{
	CRegKey key;
	// open the registry location
	if( key.Open( HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\PageSetup")) == ERROR_SUCCESS )
	{
		if( bReset )
		{
			CString szTemp;
			TCHAR getInfoString[500];
			ULONG lengthVal = 500;

			// get the old header
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "header_old" ), getInfoString, &lengthVal );
			// set header
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "header" ), szTemp );
			// remove old
			key.DeleteValue( _T( "header_old" ));

			// get the old footer
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "footer_old" ), getInfoString, &lengthVal );
			// set header
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "footer" ), szTemp );
			// remove old
			key.DeleteValue( _T( "footer_old" ));

			// get the old margin_bottom
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_bottom_old" ), getInfoString, &lengthVal );
			// set margin_top
			szTemp = getInfoString;
			key.SetStringValue( _T( "margin_bottom" ), szTemp );
			// remove old
			key.DeleteValue( _T( "margin_bottom_old" ));

			// get the old margin_top
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_top_old" ), getInfoString, &lengthVal );
			// set margin_top
			szTemp = getInfoString;
			key.SetStringValue( _T( "margin_top" ), szTemp );
			// remove old
			key.DeleteValue( _T( "margin_top_old" ));

			// get the old margin_left
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_left_old" ), getInfoString, &lengthVal );
			// set margin_left
			szTemp = getInfoString;
			key.SetStringValue( _T( "margin_left" ), szTemp );
			// remove old
			key.DeleteValue( _T( "margin_left_old" ));

			// get the old margin_right
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_right_old" ), getInfoString, &lengthVal );
			// set header
			szTemp = getInfoString;
			key.SetStringValue( _T( "margin_right" ), szTemp );
			// remove old
			key.DeleteValue( _T( "margin_right_old" ));
		}
		else
		{
			CString szTemp;
			TCHAR getInfoString[500];
			ULONG lengthVal = 500;

			// get header
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "header" ), getInfoString, &lengthVal );
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "header_old" ), szTemp );

			// get footer
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "footer" ), getInfoString, &lengthVal );
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "footer_old" ), szTemp );

			// get margin_bottom
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_bottom" ), getInfoString, &lengthVal );
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "margin_bottom_old" ), szTemp );

			// get margin_top
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_top" ), getInfoString, &lengthVal );
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "margin_top_old" ), szTemp );

			// get margin_left
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_left" ), getInfoString, &lengthVal );
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "margin_left_old" ), szTemp );

			// get margin_right
			lengthVal = 500;
			memset( getInfoString, NULL, 500 );
			key.QueryStringValue( _T( "margin_right" ), getInfoString, &lengthVal );
			szTemp = getInfoString;
			// set Page setup header
			key.SetStringValue( _T( "margin_right_old" ), szTemp );

			// set Page setup header
			key.SetStringValue( _T( "header" ), m_Printing.szHeader );
			// set Page setup footer
			key.SetStringValue( _T( "footer" ), m_Printing.szFooter );
			// set Page setup Margin bottom
			key.SetStringValue( _T( "margin_bottom" ), m_Printing.szMarginBottom );
			// set Page setup Margin top
			key.SetStringValue( _T( "margin_top" ), m_Printing.szMarginTop );
			// set Page setup Margin left
			key.SetStringValue( _T( "margin_left" ), m_Printing.szMarginLeft );
			// set Page setup Margin right
			key.SetStringValue( _T( "margin_right" ), m_Printing.szMarginRight );
		}
		return S_OK;
	}
	return S_FALSE;
}

CString CAppConfig::ConvertHTMLToPlainText(const CString& sHtml)
{
	//pull out whats within the body tags
	CString sRet(sHtml);
	sRet.MakeUpper();

	// replace end divs with return
	sRet.Replace( _T("</DIV>"), _T("\n\r") );

	int nStartCut = -1;
	int nStartBody = sRet.Find(_T("<BODY"));
	if (nStartBody != -1)
	{
		sRet = sHtml.Right(sHtml.GetLength() - nStartBody - 5);
		int nTemp = sRet.Find(_T('>'));
		nStartCut = nStartBody + nTemp + 6;
		sRet = sRet.Right(sRet.GetLength() - nTemp - 1);
	}
	sRet.MakeUpper();
	int nLength = sRet.Find(_T("</BODY"));

	//Finally do the actual cutting
	if (nLength != -1)
		sRet = sHtml.Mid(nStartCut, nLength);
	else
		sRet = sHtml;

	//Now strip all html tags
	int nStartTag = sRet.Find(_T('<'));
	int nEndTag = sRet.Find(_T('>'));
	while (nStartTag != -1 && nEndTag != -1)
	{
		sRet = sRet.Left(nStartTag) + sRet.Right(sRet.GetLength() - nEndTag - 1);
		nStartTag = sRet.Find(_T('<'));
		nEndTag = sRet.Find(_T('>'));
	}

	sRet.Trim();
	return sRet;
}

CString CAppConfig::ReadFile(CString szFile)
{
	CStdioFile fMessage;
	CString szText;

	// set wait cursor
	CWaitCursor wait;

	// if no path try local
	if( szFile.Find( '\\' ) == -1 )
	{
		CString szPath;
		int nIndex = 0;
		TCHAR cBuffer[MAX_PATH];

		memset(cBuffer, NULL, MAX_PATH);
		GetModuleFileName(NULL, cBuffer, MAX_PATH);
		
		szPath = cBuffer;
		nIndex = szPath.ReverseFind('\\') + 1;
		szFile = szPath.Left( nIndex ) + szFile;
	}

	// open message file
	if( !fMessage.Open( szFile, CFile::modeRead ))
	{
		CString szTemp;
		szTemp.Format( _T("Error opening file %s."), szFile );
		AfxMessageBox( szTemp );
		return _T("");
	}
	else
	{
		// read message file
		CString szBuf;
		while( fMessage.ReadString( szBuf ))
			szText += szBuf;
		// close file
		fMessage.Close();
	}
	// return file
	return szText;
}

CString CAppConfig::GetIDFromRequest(CString szCitation)
{
	// check for PMID
	// check string of 6 or 8 numbers
	CString szPMID;
	CString szTemp;
	TCHAR nChr = NULL;

	// set citation
	szPMID = szCitation;
	// check for id
	for( INT nCount = 0; nCount < szPMID.GetLength(); nCount++ )
	{
		nChr = szPMID[nCount];
		if( nChr <= 256 )
		{
			if( isdigit( nChr ))
				szTemp += nChr;
			if( !szTemp.IsEmpty() && !isdigit( nChr ))
			{
				INT nLen = szTemp.GetLength();
				if( nLen >= 6 && nLen <= 8 )
					break;
				else
					szTemp.Empty();
			}
		}
	}
	// check for less then number
	INT nLen = szTemp.GetLength();
	if( nLen < 6 || nLen > 8 )
		szTemp.Empty();
	// check for empty
	if( !szTemp.IsEmpty() )
		szPMID = szTemp;
	else
		szPMID.Empty();
	// return number
	return szPMID;
}

BOOL CAppConfig::PrintRequest(CDC* pdc, LONG nItemID, INT nCount)
{
	ASSERT( nItemID > 0 );

	ARTICLE_REQUEST Request;
	CString szLibrary;
	CString szSQL;
	CString szData;
	CFont newFont;
	LOGFONT lf;
	INT nLineLimit = PRINT_LINE_LIMIT;
	CString resToken;
	CString szText;
	INT curPos= 0;
	BOOL bBoldEmailOnPrint;
	BOOL bPrintFullRequest;

	INT maxw = pdc->GetDeviceCaps(HORZRES);
	INT maxh = pdc->GetDeviceCaps(VERTRES);
	// set printer Y
	m_nPrintY = nCount * ( maxh / 3 );
	// save DC
	INT nSaved = pdc->SaveDC();

	// fetch record
	if( m_SQL.GetFullRequest( &Request, nItemID ))
	{
		LIBRARY_LOOKUP Library;
		PRIORITY_LOOKUP Priority;
		METHOD_LOOKUP Method;
		COLLECTION_LOOKUP Collection;

		// if claimed
		if( Request.m_staff_id > 0 )
		{
			//staff description
			STAFF_SET Staff;
			m_SQL.GetStaffSet( &Staff, Request.m_staff_id );
			// get variables
			bBoldEmailOnPrint = Staff.m_bold_email_print;
			bPrintFullRequest = Staff.m_print_full_request;
		}
		else
		{
			bBoldEmailOnPrint = TRUE;
			bPrintFullRequest = TRUE;
		}

		// description_library
		m_SQL.GetDescriptionLibrary( &Library, Request.m_library_id );
		szLibrary = Library.szName;

		// Describe a 10-point truetype font of normal weight
		memset( &lf, 0, sizeof( lf ));
		lf.lfHeight = -MulDiv( 10, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
		lf.lfWeight = FW_NORMAL;
		lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		// create and select it
		newFont.Detach();
		if( newFont.CreateFontIndirect( &lf ))
			pdc->SelectObject( &newFont );

		// date
		szText.Format( _T("%s"), Request.m_date_in.Format( m_General.szDateFormat ) );
		PrintLine( pdc, szText );

		// Describe a 18-point truetype font of normal weight
		memset( &lf, 0, sizeof( lf ));
		lf.lfHeight = -MulDiv( 18, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
		lf.lfWeight = FW_NORMAL;
		lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		// create and select it
		newFont.Detach();
		if( newFont.CreateFontIndirect( &lf ))
			pdc->SelectObject( &newFont );

		// print user name
		szData = Request.m_users_name;
		PrintLine( pdc, szData );

		//description_priority
		m_SQL.GetDescriptionPriority( &Priority, Request.m_priority_id );
		// format text out
		szData.Format( _T("%s"), Priority.szPrintNotice );
		//print
		if( !szData.IsEmpty() )
		{
			PrintLine( pdc, szData );
			nLineLimit = PRINT_LINE_LIMIT - 2;
		}

		//description_method
		m_SQL.GetDescriptionMethod( &Method, Request.m_method_id );
		// print paper or electronic
		if( !Method.szPrintNotice.IsEmpty() )
		{
			PrintLine( pdc, Method.szPrintNotice );
			nLineLimit = PRINT_LINE_LIMIT - 2;
		}

		// description_collection
		m_SQL.GetDescriptionCollection( &Collection, Request.m_collection_id );
		//print
		if( !Collection.szPrintNotice.IsEmpty() )
		{
			PrintLine( pdc, Collection.szPrintNotice );
			nLineLimit = PRINT_LINE_LIMIT - 2;
		}

		// Describe a 10-point truetype font of normal weight
		memset( &lf, 0, sizeof( lf ));
		lf.lfHeight = -MulDiv( 10, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
		lf.lfWeight = FW_NORMAL;
		lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		// create and select it
		newFont.Detach();
		if( newFont.CreateFontIndirect( &lf ))
			pdc->SelectObject( &newFont );

		// set new position
		m_nPrintY += 50;

		// print line
		pdc->MoveTo( 0, m_nPrintY );
		pdc->LineTo( maxw, m_nPrintY );
		// set new position
		m_nPrintY += 50;

		// draw barcode
		CCode39 upc;

		szData.Format( _T("%.9d"), Request.m_id );
		CBitmapDC bitmapDC( maxw, 300, pdc );
		upc.LoadData( szData, 0.012, 0.5, bitmapDC.m_hDC, 0, 0, 3 );
		upc.DrawBitmap();
		// move over
		if( maxw > 2000 )
			pdc->BitBlt( 100, m_nPrintY, maxw, 300, &bitmapDC, 0, 0, SRCCOPY );
		else
			pdc->BitBlt( 0, m_nPrintY, maxw, 300, &bitmapDC, 0, 0, SRCCOPY );

		// set printer Y
		if( maxw < 2000 )
			m_nPrintY += 100;
		else
			m_nPrintY += 350;

		// format text out
		szData.Format( _T("%.8ld   %s"), Request.m_id, Method.szDescription );
		// print
		PrintLine( pdc, szData );

		// print a spacer
		PrintLine( pdc, _T(" ") );

		// Describe a 12-point truetype font of bold weight
		memset( &lf, 0, sizeof( lf ));
		lf.lfHeight = -MulDiv( 12, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
		lf.lfWeight = FW_SEMIBOLD;
		lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		// create and select it
		newFont.Detach();
		if( newFont.CreateFontIndirect( &lf ))
			pdc->SelectObject( &newFont );

		// print special
		if( Request.m_instructions.GetLength() > 1 )
		{
			szData.Format( _T("<< %s >>"), Request.m_instructions );
			PrintLine( pdc, szData );
			PrintLine( pdc, _T(" ") );
		}

		// Describe a 10-point truetype font of normal weight
		memset( &lf, 0, sizeof( lf ));
		lf.lfHeight = -MulDiv( 10, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
		lf.lfWeight = FW_NORMAL;
		lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		// create and select it
		newFont.Detach();
		if( newFont.CreateFontIndirect( &lf ))
			pdc->SelectObject( &newFont );

		// if bold email create font
		if( bBoldEmailOnPrint )
		{
			// Describe a 10-point truetype font of bold weight
			memset( &lf, 0, sizeof( lf ));
			lf.lfHeight = -MulDiv( 10, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
			lf.lfWeight = FW_SEMIBOLD;
			lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
			// create and select it
			newFont.Detach();
			if( newFont.CreateFontIndirect( &lf ))
				pdc->SelectObject( &newFont );
		}

		szText.Format( _T("%s"), Request.m_users_email );
		PrintLine( pdc, szText );

		// if bold email create font
		if( bBoldEmailOnPrint )
		{
			// Describe a 10-point truetype font of normal weight
			memset( &lf, 0, sizeof( lf ));
			lf.lfHeight = -MulDiv( 10, pdc->GetDeviceCaps( LOGPIXELSY ), 72 );
			lf.lfWeight = FW_NORMAL;
			lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
			// create and select it
			newFont.Detach();
			if( newFont.CreateFontIndirect( &lf ))
				pdc->SelectObject( &newFont );
		}

		// print contact info
		szText.Format( _T("%s   %s"), Request.m_users_phone, Request.m_users_company );
		PrintLine( pdc, szText );

		// print address
		if( !Method.nElectronic )
		{
			if( !Request.m_location.IsEmpty() )
			{
				szText.Format( _T("%s"), Request.m_location );
				PrintLine( pdc, szText );
			}
			else
			{
				if( !Request.m_users_address1.IsEmpty() )
				{
					szText.Format( _T("%s"), Request.m_users_address1 );
					PrintLine( pdc, szText );
				}
				if( !Request.m_users_address2.IsEmpty() )
				{
					szText.Format( _T("%s"), Request.m_users_address2 );
					PrintLine( pdc, szText );
				}
			}
		}

		// add blank line
		PrintLine( pdc, _T(" ") );

		// source
		if( !Request.m_source.IsEmpty() )
		{
			PrintLine( pdc, Request.m_source );
			// print a spacer
			PrintLine( pdc, _T(" ") );
		}
		// get first string	
		szText.Format( _T("%s\n\r"), Request.m_request );
		resToken = szText.Tokenize( _T("\n\r"), curPos );
		// print lines
		INT nLines = 0;
		// check print full ciations
		if( bPrintFullRequest )
			nLineLimit = 100;
		// print lines
		while( resToken != _T("") )
		{
			// print line		
			if( nLines < nLineLimit )
			{
				// check for empty
				if( !resToken.IsEmpty() )
				{
					PrintLine( pdc, resToken );
					// increment lines
					nLines++;
				}
			}
			resToken = szText.Tokenize( _T("\n\r"), curPos );
		}

		// print a spacer
		PrintLine( pdc, _T(" ") );

		// print notes
		if( Request.m_notes.GetLength() > 1 && nLines < nLineLimit )
		{		
			curPos= 0;
			// convert to plain text

			szData = ConvertHTMLToPlainText( Request.m_notes );
			// get first string	
			szText.Format( _T("NOTES:\n\r%s\n\r"), szData );
			resToken = szText.Tokenize( _T("\n\r"), curPos );
			while( resToken != _T("") )
			{
				// print line		
				if( nLines < nLineLimit )
					PrintLine( pdc, resToken );
				resToken = szText.Tokenize( _T("\n\r"), curPos );
				// increment lines
				nLines++;
			}
		}
		// pad
		while( m_nPrintY < 800 )
			PrintLine( pdc, _T(" ") );
		// print line
		m_nPrintY += 200;
		pdc->MoveTo( 0, m_nPrintY );
		pdc->LineTo( maxw, m_nPrintY );

		// restore DC
		pdc->RestoreDC( nSaved );

		return TRUE;
	}
	return FALSE;
}

INT CAppConfig::PrintLine(CDC* pdc, CString szLine)
{
	INT maxw = pdc->GetDeviceCaps(HORZRES);
	INT maxh = pdc->GetDeviceCaps(VERTRES);
	INT lineHeight = pdc->GetTextExtent( szLine ).cy;
	INT nLineLength = pdc->GetTextExtent( szLine ).cx;

	if( nLineLength > maxw )
	{
		CString stringtoprint = szLine;
		// check for no breaks
		if( stringtoprint.Find( _T(" ") ) == -1 )
		{
			INT nT = pdc->GetTextExtent( _T(" ") ).cx;
			INT nLength = stringtoprint.GetLength();
			INT nCount = nLineLength / maxw;
			for( INT nIndex = 1; nIndex <= nCount; nIndex++ )
				stringtoprint.Insert( nLength / (nCount + 1) * nIndex, _T("   ") );
		}
		// loop and print
		while( pdc->GetTextExtent( stringtoprint ).cx > maxw )
		{
			CString sub = stringtoprint;
			while( pdc->GetTextExtent(sub).cx > maxw )
			{
				int len = sub.GetLength() - 1;
				int newlen = len;

				while( TRUE )
				{					
					sub = sub.Left( newlen );
					if( sub.Right(1) == ' ' )
						break;
					if( newlen <= 0 )
					{
						sub = sub.Left( len );
						break;
					}
					newlen--;
				}
			}
			// Call ourself multiple times till all chars are printed
			PrintLine( pdc, sub );
			stringtoprint = stringtoprint.Right( stringtoprint.GetLength() - sub.GetLength() );
		}
		// Also print the last line
		PrintLine( pdc, stringtoprint );
	}
	else
	{
		if( maxw > 2000 )
			pdc->TextOut( 100, m_nPrintY, szLine );
		else
			pdc->TextOut( 0, m_nPrintY, szLine );
		m_nPrintY += lineHeight;
	}
	// return current Y
	return m_nPrintY;
}

BOOL CAppConfig::GetLDAPStaff(CString szServer, CString szUser, CString szPass, LDAP_STAFF_INFO* pStaffInfo)
{
	BOOL bRetVal = FALSE;
	HRESULT hr = S_OK; // COM result variable
	ADS_SEARCH_COLUMN col;  // COL for iterations
	DWORD dwCount = 0;

	// search string
	CString szSearch;

	// Interface Pointers.
	IDirectorySearch *pDSSearch = NULL;

	// Initialize COM.
	CoInitialize(0);

	// search for common name
	szSearch.Format( _T("cn=%s"), szUser );

	// Open a connection with server.
	hr = ADsOpenObject( szServer, szUser, szPass, ADS_SECURE_AUTHENTICATION, IID_IDirectorySearch, (void **)&pDSSearch );
	if( SUCCEEDED(hr) )
	{
		// TODO: Make search string configurable via INI file.
		LPWSTR pszAttr[] = { L"cn", L"mail", L"telephonenumber", };
		ADS_SEARCH_HANDLE hSearch;
		DWORD dwCount = 0;
		DWORD dwAttrNameSize = sizeof(pszAttr)/sizeof(LPWSTR);
		// Search for all objects
		hr = pDSSearch->ExecuteSearch( (LPTSTR)(LPCTSTR)szSearch, pszAttr, dwAttrNameSize, &hSearch );
		// get information
		if( SUCCEEDED(hr) )
		{
			// only need once
			pDSSearch->GetFirstRow( hSearch);
			// Get the properties.
			if( pDSSearch->GetColumn( hSearch, L"cn", &col ) == S_OK )
				pStaffInfo->szLANID = col.pADsValues->CaseExactString;
			if( pDSSearch->GetColumn( hSearch, L"mail", &col ) == S_OK )
				pStaffInfo->szEmail = col.pADsValues->CaseExactString;
			if( pDSSearch->GetColumn( hSearch, L"telephonenumber", &col ) == S_OK )
				pStaffInfo->szPhone = col.pADsValues->CaseExactString;
			// return true
			bRetVal = TRUE;
		}
		else
		{
			// return false
			bRetVal = FALSE;
		}
		// release ADS
		pDSSearch->CloseSearchHandle(hSearch);
		pDSSearch->Release();
	}
	// release COM
	CoUninitialize();
	return bRetVal;
}

BOOL CAppConfig::GetLDAPUser(CString szUser, LDAP_USER_INFO* pUserInfo, BOOL bCheck)
{
	BOOL bRetVal = FALSE;
	HRESULT hr = S_OK; // COM result variable
	ADS_SEARCH_COLUMN col;  // COL for iterations
	DWORD dwCount = 0;

	// Add code to securely retrieve the username and password or leave both as NULL to use the default security context.
	LPWSTR szUsername = NULL; // Username
	LPWSTR szPassword = NULL; // Password

	// search string
	CString szSearch;

	// Interface Pointers.
	IDirectorySearch *pDSSearch = NULL;

	// Initialize COM.
	CoInitialize(0);

	// check for email
	if( szUser.Find( _T("@") ) >=0 )
		szSearch.Format( _T("mail=%s"), szUser );
	else if( szUser.Find( _T(",") ) >=0 )
		szSearch.Format( _T("displayname=%s*"), szUser );
	else
		szSearch.Format( _T("cn=%s"), szUser );

	// Open a connection with server.
	hr = ADsOpenObject( m_LDAP.szServer, szUsername, szPassword, ADS_SECURE_AUTHENTICATION, IID_IDirectorySearch, (void **)&pDSSearch );
	if( SUCCEEDED(hr) )
	{
		LPWSTR pszAttr[] = { L"cn", L"givenname", L"sn", L"mail", L"displayname", L"telephonenumber", L"physicaldeliveryofficename", L"pager" };
		ADS_SEARCH_HANDLE hSearch;
		DWORD dwCount = 0;
		DWORD dwAttrNameSize = sizeof(pszAttr)/sizeof(LPWSTR);
		// Search for all objects
		hr = pDSSearch->ExecuteSearch( (LPTSTR)(LPCTSTR)szSearch, pszAttr, dwAttrNameSize, &hSearch );
		// get information
		if( SUCCEEDED(hr) )
		{
			// only need once
			while( pDSSearch->GetNextRow( hSearch) != S_ADS_NOMORE_ROWS )
			{
				// Get the properties.
				if( pDSSearch->GetColumn( hSearch, L"cn", &col ) == S_OK )
					pUserInfo->szLANID = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"givenname", &col ) == S_OK )
					pUserInfo->szFirstName = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"sn", &col ) == S_OK )
					pUserInfo->szLastName = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"mail", &col ) == S_OK )
					pUserInfo->szEmail = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"displayname", &col ) == S_OK )
					pUserInfo->szFullName = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"telephonenumber", &col ) == S_OK )
					pUserInfo->szPhone = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"physicaldeliveryofficename", &col ) == S_OK )
					pUserInfo->szAddress = col.pADsValues->CaseExactString;
				if( pDSSearch->GetColumn( hSearch, L"pager", &col ) == S_OK )
					pUserInfo->szPager = col.pADsValues->CaseExactString;
				// set full name
				pUserInfo->szName.Format( _T("%s %s"), pUserInfo->szFirstName, pUserInfo->szLastName );
				// add it
				dwCount++;

				// no check
				if( !bCheck )
					return TRUE;

				// found user
				CString szUserInfo;
				szUserInfo.Format( _T("User Found:\n\nLANID: %s\nName: %s\nEmail: %s\nPhone: %s\nAddress: %s\nPager: %s\n\nIs this the correct person?"), 
					pUserInfo->szLANID, pUserInfo->szName, pUserInfo->szEmail, pUserInfo->szPhone, pUserInfo->szAddress, pUserInfo->szPager );
				INT nFound = AfxMessageBox( szUserInfo, MB_YESNOCANCEL );
				if( nFound == IDYES )
					return TRUE;
				if( nFound == IDCANCEL )
					break;
			}
			// return false
			bRetVal = FALSE;
		}
		// release ADS
		pDSSearch->CloseSearchHandle(hSearch);
		pDSSearch->Release();
	}
	// release COM
	CoUninitialize();
	return bRetVal;
}

INT_PTR CAppConfig::GetHelpLinks(CStringArray &csaHelpLinks)
{
	// read keys from INI
	return ReadINISectionKeys( csaHelpLinks, _T( "HelpLinks" ));
}
